diff --git a/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java b/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java index f14cacd16bc..7ca787a1241 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java +++ b/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java @@ -4,6 +4,7 @@ package mage.game; import mage.constants.MultiplayerAttackOption; import mage.constants.RangeOfInfluence; import mage.game.match.MatchType; +import mage.game.mulligan.VancouverMulligan; /** * @@ -14,7 +15,7 @@ public class FreeForAll extends GameImpl { private int numPlayers; public FreeForAll(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { - super(attackOption, range, freeMulligans, startLife); + super(attackOption, range, new VancouverMulligan(freeMulligans), startLife); } public FreeForAll(final FreeForAll game) { diff --git a/Mage.Server.Plugins/Mage.Game.MomirDuel/src/mage/game/MomirDuel.java b/Mage.Server.Plugins/Mage.Game.MomirDuel/src/mage/game/MomirDuel.java index 8ebf139746e..61705e32feb 100644 --- a/Mage.Server.Plugins/Mage.Game.MomirDuel/src/mage/game/MomirDuel.java +++ b/Mage.Server.Plugins/Mage.Game.MomirDuel/src/mage/game/MomirDuel.java @@ -13,6 +13,7 @@ import mage.constants.RangeOfInfluence; import mage.constants.Zone; import mage.game.command.emblems.MomirEmblem; import mage.game.match.MatchType; +import mage.game.mulligan.VancouverMulligan; import mage.game.turn.TurnMod; import mage.players.Player; @@ -23,7 +24,7 @@ import mage.players.Player; public class MomirDuel extends GameImpl { public MomirDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { - super(attackOption, range, freeMulligans, startLife); + super(attackOption, range, new VancouverMulligan(freeMulligans), startLife); } public MomirDuel(final MomirDuel game) { diff --git a/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java b/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java index 542b4925c2e..b6efd57c2a3 100644 --- a/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java +++ b/Mage.Server.Plugins/Mage.Game.MomirGame/src/mage/game/MomirGame.java @@ -13,6 +13,7 @@ import mage.constants.RangeOfInfluence; import mage.constants.Zone; import mage.game.command.emblems.MomirEmblem; import mage.game.match.MatchType; +import mage.game.mulligan.VancouverMulligan; import mage.game.turn.TurnMod; import mage.players.Player; @@ -25,7 +26,7 @@ public class MomirGame extends GameImpl { private int numPlayers; public MomirGame(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { - super(attackOption, range, freeMulligans, startLife); + super(attackOption, range, new VancouverMulligan(freeMulligans), startLife); } public MomirGame(final MomirGame game) { diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java index d3fe05e57ce..2443e9b4d45 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java @@ -6,12 +6,18 @@ import mage.constants.MultiplayerAttackOption; import mage.constants.PhaseStep; import mage.constants.RangeOfInfluence; import mage.game.match.MatchType; +import mage.game.mulligan.Mulligan; +import mage.game.mulligan.VancouverMulligan; import mage.game.turn.TurnMod; public class TwoPlayerDuel extends GameImpl { public TwoPlayerDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { - super(attackOption, range, freeMulligans, startLife); + this(attackOption, range, new VancouverMulligan(freeMulligans), startLife); + } + + public TwoPlayerDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { + super(attackOption, range, mulligan, startLife); } public TwoPlayerDuel(final TwoPlayerDuel game) { diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java index 83640eaf629..4772323460b 100644 --- a/Mage/src/main/java/mage/game/Game.java +++ b/Mage/src/main/java/mage/game/Game.java @@ -25,7 +25,9 @@ import mage.game.events.GameEvent; import mage.game.events.Listener; import mage.game.events.PlayerQueryEvent; import mage.game.events.TableEvent; +import mage.game.match.Match; import mage.game.match.MatchType; +import mage.game.mulligan.Mulligan; import mage.game.permanent.Battlefield; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -472,4 +474,7 @@ public interface Game extends MageItem, Serializable { int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable); int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List appliedEffects); + + Mulligan getMulligan(); + } diff --git a/Mage/src/main/java/mage/game/GameCanadianHighlanderImpl.java b/Mage/src/main/java/mage/game/GameCanadianHighlanderImpl.java index 03b905bcc01..b9719e1a6b1 100644 --- a/Mage/src/main/java/mage/game/GameCanadianHighlanderImpl.java +++ b/Mage/src/main/java/mage/game/GameCanadianHighlanderImpl.java @@ -5,16 +5,15 @@ import java.util.*; import mage.constants.MultiplayerAttackOption; import mage.constants.PhaseStep; import mage.constants.RangeOfInfluence; +import mage.game.mulligan.CanadianHighlanderMulligan; +import mage.game.mulligan.Mulligan; import mage.game.turn.TurnMod; import mage.players.Player; public abstract class GameCanadianHighlanderImpl extends GameImpl { - protected boolean startingPlayerSkipsDraw = true; - protected Map usedMulligans = new LinkedHashMap<>(); - public GameCanadianHighlanderImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { - super(attackOption, range, 0, startLife); + super(attackOption, range, new CanadianHighlanderMulligan(freeMulligans), startLife); } public GameCanadianHighlanderImpl(final GameCanadianHighlanderImpl game) { @@ -27,110 +26,4 @@ public abstract class GameCanadianHighlanderImpl extends GameImpl { state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); } - private String getNextMulligan(String mulligan) { - switch (mulligan) { - case "7": - return "6a"; - case "6a": - return "6b"; - case "6b": - return "5a"; - case "5a": - return "5b"; - case "5b": - return "4a"; - case "4a": - return "4b"; - case "4b": - return "3a"; - case "3a": - return "3b"; - case "3b": - return "2a"; - case "2a": - return "2b"; - case "2b": - return "1a"; - case "1a": - return "1b"; - } - return "0"; - } - - private int getNextMulliganNum(String mulligan) { - switch (mulligan) { - case "7": - return 6; - case "6a": - return 6; - case "6b": - return 5; - case "5a": - return 5; - case "5b": - return 4; - case "4a": - return 4; - case "4b": - return 3; - case "3a": - return 3; - case "3b": - return 2; - case "2a": - return 2; - case "2b": - return 1; - case "1a": - return 1; - } - return 0; - } - - @Override - public int mulliganDownTo(UUID playerId) { - Player player = getPlayer(playerId); - int deduction = 1; - int numToMulliganTo = -1; - if (usedMulligans != null) { - String mulliganCode = "7"; - if (usedMulligans.containsKey(player.getId())) { - mulliganCode = usedMulligans.get(player.getId()); - } - numToMulliganTo = getNextMulliganNum(mulliganCode); - } - if (numToMulliganTo == -1) { - return player.getHand().size() - deduction; - } - return numToMulliganTo; - } - - @Override - public void mulligan(UUID playerId) { - Player player = getPlayer(playerId); - int numCards = player.getHand().size(); - int numToMulliganTo = numCards; - player.getLibrary().addAll(player.getHand().getCards(this), this); - player.getHand().clear(); - player.shuffleLibrary(null, this); - if (usedMulligans != null) { - String mulliganCode = "7"; - if (usedMulligans.containsKey(player.getId())) { - mulliganCode = usedMulligans.get(player.getId()); - } - numToMulliganTo = getNextMulliganNum(mulliganCode); - usedMulligans.put(player.getId(), getNextMulligan(mulliganCode)); - } - fireInformEvent(new StringBuilder(player.getLogName()) - .append(" mulligans to ") - .append(Integer.toString(numToMulliganTo)) - .append(numToMulliganTo == 1 ? " card" : " cards").toString()); - player.drawCards(numToMulliganTo, this); - } - - @Override - public void endMulligan(UUID playerId) { - super.endMulligan(playerId); - } - } diff --git a/Mage/src/main/java/mage/game/GameCommanderImpl.java b/Mage/src/main/java/mage/game/GameCommanderImpl.java index 265dda7dc0d..f3034597ccd 100644 --- a/Mage/src/main/java/mage/game/GameCommanderImpl.java +++ b/Mage/src/main/java/mage/game/GameCommanderImpl.java @@ -12,6 +12,7 @@ import mage.constants.MultiplayerAttackOption; import mage.constants.PhaseStep; import mage.constants.RangeOfInfluence; import mage.constants.Zone; +import mage.game.mulligan.VancouverMulligan; import mage.game.turn.TurnMod; import mage.players.Player; import mage.watchers.common.CommanderInfoWatcher; @@ -25,7 +26,7 @@ public abstract class GameCommanderImpl extends GameImpl { protected boolean startingPlayerSkipsDraw = true; public GameCommanderImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { - super(attackOption, range, freeMulligans, startLife); + super(attackOption, range, new VancouverMulligan(freeMulligans), startLife); } public GameCommanderImpl(final GameCommanderImpl game) { diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 25ec612248d..663711bc2ee 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -40,6 +40,8 @@ import mage.game.command.Emblem; import mage.game.command.Plane; import mage.game.events.*; import mage.game.events.TableEvent.EventType; +import mage.game.mulligan.LondonMulligan; +import mage.game.mulligan.Mulligan; import mage.game.permanent.Battlefield; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -108,8 +110,8 @@ public abstract class GameImpl implements Game, Serializable { protected UUID winnerId; protected RangeOfInfluence range; - protected int freeMulligans; - protected Map usedFreeMulligans = new LinkedHashMap<>(); + protected Mulligan mulligan; + protected MultiplayerAttackOption attackOption; protected GameOptions gameOptions; protected String startMessage; @@ -142,10 +144,10 @@ public abstract class GameImpl implements Game, Serializable { // used to proceed player conceding requests private final LinkedList concedingPlayers = new LinkedList<>(); // used to handle asynchronous request of a player to leave the game - public GameImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { + public GameImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, Mulligan mulligan, int startLife) { this.id = UUID.randomUUID(); this.range = range; - this.freeMulligans = freeMulligans; + this.mulligan = mulligan; this.attackOption = attackOption; this.state = new GameState(); this.startLife = startLife; @@ -163,7 +165,7 @@ public abstract class GameImpl implements Game, Serializable { this.startingPlayerId = game.startingPlayerId; this.winnerId = game.winnerId; this.range = game.range; - this.freeMulligans = game.freeMulligans; + this.mulligan = game.getMulligan().copy(); this.attackOption = game.attackOption; this.state = game.state.copy(); this.gameCards = game.gameCards; @@ -968,50 +970,7 @@ public abstract class GameImpl implements Game, Serializable { } //20091005 - 103.4 - List keepPlayers = new ArrayList<>(); - List mulliganPlayers = new ArrayList<>(); - do { - mulliganPlayers.clear(); - for (UUID playerId : state.getPlayerList(startingPlayerId)) { - if (!keepPlayers.contains(playerId)) { - Player player = getPlayer(playerId); - boolean keep = true; - while (true) { - if (player.getHand().isEmpty()) { - break; - } - GameEvent event = new GameEvent(GameEvent.EventType.CAN_TAKE_MULLIGAN, null, null, playerId); - if (!replaceEvent(event)) { - fireEvent(event); - getState().setChoosingPlayerId(playerId); - if (player.chooseMulligan(this)) { - keep = false; - } - break; - } - } - if (keep) { - endMulligan(player.getId()); - keepPlayers.add(playerId); - fireInformEvent(player.getLogName() + " keeps hand"); - } else { - mulliganPlayers.add(playerId); - fireInformEvent(player.getLogName() + " decides to take mulligan"); - } - } - } - for (UUID mulliganPlayerId : mulliganPlayers) { - mulligan(mulliganPlayerId); - } - saveState(false); - } while (!mulliganPlayers.isEmpty()); - // new scry rule - for (UUID playerId : state.getPlayerList(startingPlayerId)) { - Player player = getPlayer(playerId); - if (player != null && player.getHand().size() < startingHandSize) { - player.scry(1, null, this); - } - } + mulligan.executeMulliganPhase(this, startingHandSize); getState().setChoosingPlayerId(null); state.resetWatchers(); // watcher objects from cards are reused during match so reset all card watchers already added @@ -1169,51 +1128,17 @@ public abstract class GameImpl implements Game, Serializable { @Override public int mulliganDownTo(UUID playerId) { - Player player = getPlayer(playerId); - int deduction = 1; - if (freeMulligans > 0) { - if (usedFreeMulligans != null && usedFreeMulligans.containsKey(player.getId())) { - int used = usedFreeMulligans.get(player.getId()); - if (used < freeMulligans) { - deduction = 0; - } - } else { - deduction = 0; - } - } - return player.getHand().size() - deduction; + return mulligan.mulliganDownTo(this, playerId); } @Override public void endMulligan(UUID playerId) { + mulligan.endMulligan(this, playerId); } @Override public void mulligan(UUID playerId) { - Player player = getPlayer(playerId); - int numCards = player.getHand().size(); - player.getLibrary().addAll(player.getHand().getCards(this), this); - player.getHand().clear(); - player.shuffleLibrary(null, this); - int deduction = 1; - if (freeMulligans > 0) { - if (usedFreeMulligans.containsKey(player.getId())) { - int used = usedFreeMulligans.get(player.getId()); - if (used < freeMulligans) { - deduction = 0; - usedFreeMulligans.put(player.getId(), used + 1); - } - } else { - deduction = 0; - usedFreeMulligans.put(player.getId(), 1); - } - } - fireInformEvent(new StringBuilder(player.getLogName()) - .append(" mulligans") - .append(deduction == 0 ? " for free and draws " : " down to ") - .append((numCards - deduction)) - .append(numCards - deduction == 1 ? " card" : " cards").toString()); - player.drawCards(numCards - deduction, this); + mulligan.mulligan(this, playerId); } @Override @@ -3249,4 +3174,10 @@ public abstract class GameImpl implements Game, Serializable { } return 0; } + + @Override + public Mulligan getMulligan() { + return mulligan; + } + } diff --git a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java index eecbddd5f11..c1dd6232f6a 100644 --- a/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java +++ b/Mage/src/main/java/mage/game/GameTinyLeadersImpl.java @@ -16,6 +16,7 @@ import mage.cards.CardSetInfo; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import mage.constants.*; +import mage.game.mulligan.VancouverMulligan; import mage.game.turn.TurnMod; import mage.players.Player; import mage.watchers.common.CommanderInfoWatcher; @@ -31,7 +32,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl { protected boolean startingPlayerSkipsDraw = true; public GameTinyLeadersImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) { - super(attackOption, range, freeMulligans, startLife); + super(attackOption, range, new VancouverMulligan(freeMulligans), startLife); } public GameTinyLeadersImpl(final GameTinyLeadersImpl game) { diff --git a/Mage/src/main/java/mage/game/mulligan/CanadianHighlanderMulligan.java b/Mage/src/main/java/mage/game/mulligan/CanadianHighlanderMulligan.java new file mode 100644 index 00000000000..320f39897c9 --- /dev/null +++ b/Mage/src/main/java/mage/game/mulligan/CanadianHighlanderMulligan.java @@ -0,0 +1,124 @@ +package mage.game.mulligan; + +import mage.game.Game; +import mage.players.Player; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; + +public class CanadianHighlanderMulligan extends VancouverMulligan { + + protected Map usedMulligans = new LinkedHashMap<>(); + + public CanadianHighlanderMulligan(int freeMulligans) { + super(freeMulligans); + } + + @Override + public CanadianHighlanderMulligan copy() { + return new CanadianHighlanderMulligan(getFreeMulligans()); + } + + private String getNextMulligan(String mulligan) { + switch (mulligan) { + case "7": + return "6a"; + case "6a": + return "6b"; + case "6b": + return "5a"; + case "5a": + return "5b"; + case "5b": + return "4a"; + case "4a": + return "4b"; + case "4b": + return "3a"; + case "3a": + return "3b"; + case "3b": + return "2a"; + case "2a": + return "2b"; + case "2b": + return "1a"; + case "1a": + return "1b"; + } + return "0"; + } + + private int getNextMulliganNum(String mulligan) { + switch (mulligan) { + case "7": + return 6; + case "6a": + return 6; + case "6b": + return 5; + case "5a": + return 5; + case "5b": + return 4; + case "4a": + return 4; + case "4b": + return 3; + case "3a": + return 3; + case "3b": + return 2; + case "2a": + return 2; + case "2b": + return 1; + case "1a": + return 1; + } + return 0; + } + + @Override + public int mulliganDownTo(Game game, UUID playerId) { + Player player = game.getPlayer(playerId); + int deduction = 1; + int numToMulliganTo = -1; + if (usedMulligans != null) { + String mulliganCode = "7"; + if (usedMulligans.containsKey(player.getId())) { + mulliganCode = usedMulligans.get(player.getId()); + } + numToMulliganTo = getNextMulliganNum(mulliganCode); + } + if (numToMulliganTo == -1) { + return player.getHand().size() - deduction; + } + return numToMulliganTo; + } + + @Override + public void mulligan(Game game, UUID playerId) { + Player player = game.getPlayer(playerId); + int numCards = player.getHand().size(); + int numToMulliganTo = numCards; + player.getLibrary().addAll(player.getHand().getCards(game), game); + player.getHand().clear(); + player.shuffleLibrary(null, game); + if (usedMulligans != null) { + String mulliganCode = "7"; + if (usedMulligans.containsKey(player.getId())) { + mulliganCode = usedMulligans.get(player.getId()); + } + numToMulliganTo = getNextMulliganNum(mulliganCode); + usedMulligans.put(player.getId(), getNextMulligan(mulliganCode)); + } + game.fireInformEvent(new StringBuilder(player.getLogName()) + .append(" mulligans to ") + .append(Integer.toString(numToMulliganTo)) + .append(numToMulliganTo == 1 ? " card" : " cards").toString()); + player.drawCards(numToMulliganTo, game); + } + +} diff --git a/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java b/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java new file mode 100644 index 00000000000..b63304b4ab2 --- /dev/null +++ b/Mage/src/main/java/mage/game/mulligan/LondonMulligan.java @@ -0,0 +1,110 @@ +package mage.game.mulligan; + +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.TargetCard; + +import java.util.*; + +public class LondonMulligan extends Mulligan { + + protected Map startingHandSizes = new HashMap<>(); + + public LondonMulligan(int freeMulligans) { + super(freeMulligans); + } + + @Override + public void executeMulliganPhase(Game game, int startingHandSize) { + for (UUID playerId : game.getState().getPlayerList(game.getStartingPlayerId())) { + startingHandSizes.put(playerId, startingHandSize); + } + + super.executeMulliganPhase(game, startingHandSize); + + for (UUID playerId : game.getState().getPlayerList(game.getStartingPlayerId())) { + int handSize = startingHandSizes.get(playerId); + Player player = game.getPlayer(playerId); + if (player != null && player.getHand().size() > handSize) { + int cardsToDiscard = player.getHand().size() - handSize; + Cards cards = new CardsImpl(); + cards.addAll(player.getHand()); + TargetCard target = new TargetCard(cardsToDiscard, cardsToDiscard, Zone.HAND, + new FilterCard("cards to PUT on the BOTTOM of your library (Discard for Mulligan)")); + player.chooseTarget(Outcome.Neutral, cards, target, null, game); + player.putCardsOnBottomOfLibrary(new CardsImpl(target.getTargets()), game, null, true); + cards.removeAll(target.getTargets()); + System.out.println(cardsToDiscard); + } + } + } + + @Override + public int mulliganDownTo(Game game, UUID playerId) { + Player player = game.getPlayer(playerId); + int deduction = 1; + if (freeMulligans > 0) { + if (usedFreeMulligans != null && usedFreeMulligans.containsKey(player.getId())) { + int used = usedFreeMulligans.get(player.getId()); + if (used < freeMulligans) { + deduction = 0; + } + } else { + deduction = 0; + } + } + return startingHandSizes.get(playerId) - deduction; + } + + @Override + public void mulligan(Game game, UUID playerId) { + Player player = game.getPlayer(playerId); + int numCards = player.getHand().size(); + player.getLibrary().addAll(player.getHand().getCards(game), game); + player.getHand().clear(); + player.shuffleLibrary(null, game); + int deduction = 1; + if (freeMulligans > 0) { + if (usedFreeMulligans.containsKey(player.getId())) { + int used = usedFreeMulligans.get(player.getId()); + if (used < freeMulligans) { + deduction = 0; + usedFreeMulligans.put(player.getId(), used + 1); + } + } else { + deduction = 0; + usedFreeMulligans.put(player.getId(), 1); + } + } + startingHandSizes.put(playerId, startingHandSizes.get(playerId) - deduction); + if (deduction == 0) { + game.fireInformEvent(new StringBuilder(player.getLogName()) + .append(" mulligans for free.") + .toString()); + } else { + game.fireInformEvent(new StringBuilder(player.getLogName()) + .append(" mulligans") + .append(" down to ") + .append((numCards - deduction)) + .append(numCards - deduction == 1 ? " card" : " cards").toString()); + } + player.drawCards(numCards, game); + } + + @Override + public void endMulligan(Game game, UUID playerId) {} + + @Override + public LondonMulligan copy() { + LondonMulligan mulligan = new LondonMulligan(getFreeMulligans()); + mulligan.startingHandSizes.putAll(startingHandSizes); + return mulligan; + } + +} diff --git a/Mage/src/main/java/mage/game/mulligan/Mulligan.java b/Mage/src/main/java/mage/game/mulligan/Mulligan.java new file mode 100644 index 00000000000..c12b6b0e7a9 --- /dev/null +++ b/Mage/src/main/java/mage/game/mulligan/Mulligan.java @@ -0,0 +1,70 @@ +package mage.game.mulligan; + +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; + +import java.util.*; + +public abstract class Mulligan { + + protected final int freeMulligans; + protected final Map usedFreeMulligans = new HashMap<>(); + + public Mulligan(int freeMulligans) { + this.freeMulligans = freeMulligans; + } + + public void executeMulliganPhase(Game game, int startingHandSize) { + List keepPlayers = new ArrayList<>(); + List mulliganPlayers = new ArrayList<>(); + do { + mulliganPlayers.clear(); + for (UUID playerId : game.getState().getPlayerList(game.getStartingPlayerId())) { + if (!keepPlayers.contains(playerId)) { + Player player = game.getPlayer(playerId); + boolean keep = true; + while (true) { + if (player.getHand().isEmpty()) { + break; + } + GameEvent event = new GameEvent(GameEvent.EventType.CAN_TAKE_MULLIGAN, null, null, playerId); + if (!game.replaceEvent(event)) { + game.fireEvent(event); + game.getState().setChoosingPlayerId(playerId); + if (player.chooseMulligan(game)) { + keep = false; + } + break; + } + } + if (keep) { + game.endMulligan(player.getId()); + keepPlayers.add(playerId); + game.fireInformEvent(player.getLogName() + " keeps hand"); + } else { + mulliganPlayers.add(playerId); + game.fireInformEvent(player.getLogName() + " decides to take mulligan"); + } + } + } + for (UUID mulliganPlayerId : mulliganPlayers) { + mulligan(game, mulliganPlayerId); + } + game.saveState(false); + } while (!mulliganPlayers.isEmpty()); + } + + public abstract int mulliganDownTo(Game game, UUID playerId); + + public abstract void mulligan(Game game, UUID playerId); + + public abstract void endMulligan(Game game, UUID playerId); + + public abstract Mulligan copy(); + + public int getFreeMulligans() { + return freeMulligans; + } + +} diff --git a/Mage/src/main/java/mage/game/mulligan/ParisMulligan.java b/Mage/src/main/java/mage/game/mulligan/ParisMulligan.java new file mode 100644 index 00000000000..89ed63c087d --- /dev/null +++ b/Mage/src/main/java/mage/game/mulligan/ParisMulligan.java @@ -0,0 +1,67 @@ +package mage.game.mulligan; + +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +public class ParisMulligan extends Mulligan { + + public ParisMulligan(int freeMulligans) { + super(freeMulligans); + } + + @Override + public int mulliganDownTo(Game game, UUID playerId) { + Player player = game.getPlayer(playerId); + int deduction = 1; + if (freeMulligans > 0) { + if (usedFreeMulligans != null && usedFreeMulligans.containsKey(player.getId())) { + int used = usedFreeMulligans.get(player.getId()); + if (used < freeMulligans) { + deduction = 0; + } + } else { + deduction = 0; + } + } + return player.getHand().size() - deduction; + } + + @Override + public void mulligan(Game game, UUID playerId) { + Player player = game.getPlayer(playerId); + int numCards = player.getHand().size(); + player.getLibrary().addAll(player.getHand().getCards(game), game); + player.getHand().clear(); + player.shuffleLibrary(null, game); + int deduction = 1; + if (freeMulligans > 0) { + if (usedFreeMulligans.containsKey(player.getId())) { + int used = usedFreeMulligans.get(player.getId()); + if (used < freeMulligans) { + deduction = 0; + usedFreeMulligans.put(player.getId(), used + 1); + } + } else { + deduction = 0; + usedFreeMulligans.put(player.getId(), 1); + } + } + game.fireInformEvent(new StringBuilder(player.getLogName()) + .append(" mulligans") + .append(deduction == 0 ? " for free and draws " : " down to ") + .append((numCards - deduction)) + .append(numCards - deduction == 1 ? " card" : " cards").toString()); + player.drawCards(numCards - deduction, game); + } + + @Override + public void endMulligan(Game game, UUID playerId) {} + + @Override + public ParisMulligan copy() { + return new ParisMulligan(getFreeMulligans()); + } + +} diff --git a/Mage/src/main/java/mage/game/mulligan/VancouverMulligan.java b/Mage/src/main/java/mage/game/mulligan/VancouverMulligan.java new file mode 100644 index 00000000000..0474a4df5fe --- /dev/null +++ b/Mage/src/main/java/mage/game/mulligan/VancouverMulligan.java @@ -0,0 +1,31 @@ +package mage.game.mulligan; + +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +public class VancouverMulligan extends ParisMulligan { + + public VancouverMulligan(int freeMulligans) { + super(freeMulligans); + } + + @Override + public void executeMulliganPhase(Game game, int startingHandSize) { + super.executeMulliganPhase(game, startingHandSize); + // new scry rule + for (UUID playerId : game.getState().getPlayerList(game.getStartingPlayerId())) { + Player player = game.getPlayer(playerId); + if (player != null && player.getHand().size() < startingHandSize) { + player.scry(1, null, game); + } + } + } + + @Override + public VancouverMulligan copy() { + return new VancouverMulligan(getFreeMulligans()); + } + +}