diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 1a4376377d6..58d844b2911 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -109,6 +109,7 @@ public class Game { private final Match match; private GameStage age = GameStage.BeforeMulligan; private GameOutcome outcome; + private Game maingame = null; private final GameView view; private final Tracker tracker = new Tracker(); @@ -219,7 +220,11 @@ public class Game { changeZoneLKIInfo.clear(); } - public Game(Iterable players0, GameRules rules0, Match match0) { /* no more zones to map here */ + public Game(Iterable players0, GameRules rules0, Match match0) { + this(players0, rules0, match0, -1); + } + + public Game(Iterable players0, GameRules rules0, Match match0, int startingLife) { /* no more zones to map here */ rules = rules0; match = match0; this.id = nextId(); @@ -243,7 +248,11 @@ public class Game { allPlayers.add(pl); ingamePlayers.add(pl); - pl.setStartingLife(psc.getStartingLife()); + if (startingLife != -1) { + pl.setStartingLife(startingLife); + } else { + pl.setStartingLife(psc.getStartingLife()); + } pl.setMaxHandSize(psc.getStartingHand()); pl.setStartingHandSize(psc.getStartingHand()); @@ -430,6 +439,14 @@ public class Game { return outcome; } + public final Game getMaingame() { + return maingame; + } + + public void setMaingame(final Game maingame0) { + maingame = maingame0; + } + public ReplacementHandler getReplacementHandler() { return replacementHandler; } @@ -452,12 +469,16 @@ public class Game { result.setTurnsPlayed(getPhaseHandler().getTurn()); outcome = result; - match.addGamePlayed(this); + if (maingame == null) { + match.addGamePlayed(this); + } view.updateGameOver(this); // The log shall listen to events and generate text internally - fireEvent(new GameEventGameOutcome(result, match.getOutcomes())); + if (maingame == null) { + fireEvent(new GameEventGameOutcome(result, match.getOutcomes())); + } } public Zone getZoneOf(final Card card) { @@ -492,6 +513,14 @@ public class Game { return cards; } + public CardCollectionView getCardsInOwnedBy(final Iterable zones, Player p) { + CardCollection cards = new CardCollection(); + for (final ZoneType z : zones) { + cards.addAll(getCardsIncludePhasingIn(z)); + } + return CardLists.filter(cards, CardPredicates.isOwner(p)); + } + public boolean isCardExiled(final Card c) { return getCardsIn(ZoneType.Exile).contains(c); } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 7237d41a4af..8bba12fde2f 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -516,6 +516,18 @@ public class GameAction { c = changeZone(zoneFrom, zoneTo, c, position, cause, params); + // Move card in maingame if take card from subgame + // 720.4a + if (zoneFrom != null && zoneFrom.is(ZoneType.Sideboard) && game.getMaingame() != null) { + Card maingameCard = c.getOwner().getMappingMaingameCard(c); + if (maingameCard != null) { + if (maingameCard.getZone().is(ZoneType.Stack)) { + game.getMaingame().getStack().remove(maingameCard); + } + game.getMaingame().getAction().moveTo(ZoneType.Subgame, maingameCard, null); + } + } + if (zoneFrom == null) { c.setCastFrom(null); c.setCastSA(null); diff --git a/forge-game/src/main/java/forge/game/ability/ApiType.java b/forge-game/src/main/java/forge/game/ability/ApiType.java index 365afb52c11..f21cb010fc6 100644 --- a/forge-game/src/main/java/forge/game/ability/ApiType.java +++ b/forge-game/src/main/java/forge/game/ability/ApiType.java @@ -153,6 +153,7 @@ public enum ApiType { SkipTurn (SkipTurnEffect.class), StoreSVar (StoreSVarEffect.class), StoreMap (StoreMapEffect.class), + Subgame (SubgameEffect.class), Surveil (SurveilEffect.class), SwitchBlock (SwitchBlockEffect.class), Tap (TapEffect.class), diff --git a/forge-game/src/main/java/forge/game/ability/effects/SubgameEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SubgameEffect.java new file mode 100644 index 00000000000..486c43a2c26 --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/SubgameEffect.java @@ -0,0 +1,254 @@ +package forge.game.ability.effects; + +import java.util.*; + +import com.google.common.collect.Lists; + +import forge.card.MagicColor; +import forge.game.Game; +import forge.game.GameOutcome; +import forge.game.ability.ApiType; +import forge.game.ability.SpellAbilityEffect; +import forge.game.card.Card; +import forge.game.card.CardCollectionView; +import forge.game.event.GameEventSubgameStart; +import forge.game.event.GameEventSubgameEnd; +import forge.game.player.Player; +import forge.game.player.PlayerController; +import forge.game.player.RegisteredPlayer; +import forge.game.spellability.SpellAbility; +import forge.game.zone.PlayerZone; +import forge.game.zone.ZoneType; +import forge.item.PaperCard; +import forge.util.CardTranslation; +import forge.util.Lang; +import forge.util.Localizer; +import forge.util.collect.FCollectionView; + +public class SubgameEffect extends SpellAbilityEffect { + + private final Game createSubGame(Game maingame, int startingLife) { + List players = Lists.newArrayList(); + + // Add remaining players to subgame + for (Player p : maingame.getPlayers()) { + players.add(p.getRegisteredPlayer()); + } + + return new Game(players, maingame.getRules(), maingame.getMatch(), startingLife); + } + + private final void setCardsInZone(Player player, final ZoneType zoneType, final CardCollectionView oldCards, boolean addMapping) { + PlayerZone zone = player.getZone(zoneType); + List newCards = Lists.newArrayList(); + for (final Card card : oldCards) { + if (card.isToken() || card.isCopiedSpell()) continue; + Card newCard = Card.fromPaperCard(card.getPaperCard(), player); + newCards.add(newCard); + if (addMapping) { + // Build mapping between maingame cards and subgame cards, + // so when subgame pick a card from maingame (like Wish effects), + // The maingame card will also be moved. + // (Will be move to Subgame zone, which will be added back to libary after subgame ends.) + player.addMaingameCardMapping(newCard, card); + } + } + zone.setCards(newCards); + } + + private final void initVariantsZonesSubgame(final Game subgame, final Player maingamePlayer, final Player player) { + PlayerZone com = player.getZone(ZoneType.Command); + RegisteredPlayer registeredPlayer = player.getRegisteredPlayer(); + + // Vanguard + if (registeredPlayer.getVanguardAvatars() != null) { + for(PaperCard avatar:registeredPlayer.getVanguardAvatars()) { + com.add(Card.fromPaperCard(avatar, player)); + } + } + + // Commander + List commanders = Lists.newArrayList(); + final CardCollectionView commandCards = maingamePlayer.getCardsIn(ZoneType.Command); + for (final Card card : commandCards) { + if (card.isCommander()) { + Card cmd = Card.fromPaperCard(card.getPaperCard(), player); + if (cmd.hasKeyword("If CARDNAME is your commander, choose a color before the game begins.")) { + List colorChoices = new ArrayList<>(MagicColor.Constant.ONLY_COLORS); + String prompt = Localizer.getInstance().getMessage("lblChooseAColorFor", cmd.getName()); + List chosenColors; + SpellAbility cmdColorsa = new SpellAbility.EmptySa(ApiType.ChooseColor, cmd, player); + chosenColors = player.getController().chooseColors(prompt,cmdColorsa, 1, 1, colorChoices); + cmd.setChosenColors(chosenColors); + subgame.getAction().nofityOfValue(cmdColorsa, cmd, Localizer.getInstance().getMessage("lblPlayerPickedChosen", player.getName(), Lang.joinHomogenous(chosenColors)), player); + } + cmd.setCommander(true); + com.add(cmd); + commanders.add(cmd); + com.add(Player.createCommanderEffect(subgame, cmd)); + } + } + if (!commanders.isEmpty()) { + player.setCommanders(commanders); + } + + // Conspiracies + // 720.2 doesn't mention Conspiracy cards so I guess they don't move + } + + private void prepareAllZonesSubgame(final Game maingame, final Game subgame) { + final FCollectionView players = subgame.getPlayers(); + final FCollectionView maingamePlayers = maingame.getPlayers(); + final List outsideZones = Arrays.asList(ZoneType.Hand, ZoneType.Battlefield, + ZoneType.Graveyard, ZoneType.Exile, ZoneType.Stack, ZoneType.Sideboard, ZoneType.Ante); + + for (int i = 0; i < players.size(); i++) { + final Player player = players.get(i); + final Player maingamePlayer = maingamePlayers.get(i); + + // Library + setCardsInZone(player, ZoneType.Library, maingamePlayer.getCardsIn(ZoneType.Library), false); + + // Sideboard + // 720.4 + final CardCollectionView outsideCards = maingame.getCardsInOwnedBy(outsideZones, maingamePlayer); + if (!outsideCards.isEmpty()) { + setCardsInZone(player, ZoneType.Sideboard, outsideCards, true); + + // Assign Companion + PlayerController person = player.getController(); + Card companion = player.assignCompanion(subgame, person); + // Create an effect that lets you cast your companion from your sideboard + if (companion != null) { + PlayerZone commandZone = player.getZone(ZoneType.Command); + companion = subgame.getAction().moveTo(ZoneType.Command, companion, null); + commandZone.add(Player.createCompanionEffect(subgame, companion)); + + player.updateZoneForView(commandZone); + } + } + + // Schemes + setCardsInZone(player, ZoneType.SchemeDeck, maingamePlayer.getCardsIn(ZoneType.SchemeDeck), false); + + // Planes + setCardsInZone(player, ZoneType.PlanarDeck, maingamePlayer.getCardsIn(ZoneType.PlanarDeck), false); + + // Vanguard and Commanders + initVariantsZonesSubgame(subgame, maingamePlayer, player); + + player.shuffle(null); + player.getZone(ZoneType.SchemeDeck).shuffle(); + player.getZone(ZoneType.PlanarDeck).shuffle(); + } + } + + @Override + public void resolve(SpellAbility sa) { + final Card hostCard = sa.getHostCard(); + final Game maingame = hostCard.getGame(); + + int startingLife = -1; + if (sa.hasParam("StartingLife")) { + startingLife = Integer.parseInt(sa.getParam("StartingLife")); + } + Game subgame = createSubGame(maingame, startingLife); + subgame.setMaingame(maingame); + + String startMessage = Localizer.getInstance().getMessage("lblSubgameStart", + CardTranslation.getTranslatedName(hostCard.getName())); + maingame.fireEvent(new GameEventSubgameStart(subgame, startMessage)); + + prepareAllZonesSubgame(maingame, subgame); + subgame.getAction().startGame(null, null); + subgame.clearCaches(); + + // Find out winners and losers + final GameOutcome outcome = subgame.getOutcome(); + List winPlayers = Lists.newArrayList(); + List notWinPlayers = Lists.newArrayList(); + StringBuilder sbWinners = new StringBuilder(); + StringBuilder sbLosers = new StringBuilder(); + for (Player p : maingame.getPlayers()) { + if (outcome.isWinner(p.getRegisteredPlayer())) { + if (!winPlayers.isEmpty()) { + sbWinners.append(", "); + } + sbWinners.append(p.getName()); + winPlayers.add(p); + } else { + if (!notWinPlayers.isEmpty()) { + sbLosers.append(", "); + } + sbLosers.append(p.getName()); + notWinPlayers.add(p); + } + } + + if (sa.hasParam("RememberPlayers")) { + final String param = sa.getParam("RememberPlayers"); + if (param.equals("Win")) { + for (Player p : winPlayers) { + hostCard.addRemembered(p); + } + } else if (param.equals("NotWin")) { + for (Player p : notWinPlayers) { + hostCard.addRemembered(p); + } + } + } + + + String endMessage = outcome.isDraw() ? Localizer.getInstance().getMessage("lblSubgameEndDraw") : + Localizer.getInstance().getMessage("lblSubgameEnd", sbWinners.toString(), sbLosers.toString()); + maingame.fireEvent(new GameEventSubgameEnd(maingame, endMessage)); + + // Setup maingame library + final FCollectionView subgamePlayers = subgame.getRegisteredPlayers(); + final FCollectionView players = maingame.getPlayers(); + for (int i = 0; i < players.size(); i++) { + final Player subgamePlayer = subgamePlayers.get(i); + final Player player = players.get(i); + + // All cards moved to Subgame Zone will be put into library when subgame ends. + // 720.5 + final CardCollectionView movedCards = player.getCardsIn(ZoneType.Subgame); + PlayerZone library = player.getZone(ZoneType.Library); + for (final Card card : movedCards) { + library.add(card); + } + player.getZone(ZoneType.Subgame).removeAllCards(true); + + // Move commander if it is no longer in subgame's commander zone + // 720.5c + List subgameCommanders = Lists.newArrayList(); + List movedCommanders = Lists.newArrayList(); + for (final Card card : subgamePlayer.getCardsIn(ZoneType.Command)) { + if (card.isCommander()) { + subgameCommanders.add(card); + } + } + for (final Card card : player.getCardsIn(ZoneType.Command)) { + if (card.isCommander()) { + boolean isInSubgameCommand = false; + for (final Card subCard : subgameCommanders) { + if (card.getName().equals(subCard.getName())) { + isInSubgameCommand = true; + } + } + if (!isInSubgameCommand) { + movedCommanders.add(card); + } + } + } + for (final Card card : movedCommanders) { + maingame.getAction().moveTo(ZoneType.Library, card, null); + } + + player.shuffle(sa); + player.getZone(ZoneType.SchemeDeck).shuffle(); + player.getZone(ZoneType.PlanarDeck).shuffle(); + } + } + +} diff --git a/forge-game/src/main/java/forge/game/event/GameEventSubgameEnd.java b/forge-game/src/main/java/forge/game/event/GameEventSubgameEnd.java new file mode 100644 index 00000000000..5fcaf2a9256 --- /dev/null +++ b/forge-game/src/main/java/forge/game/event/GameEventSubgameEnd.java @@ -0,0 +1,18 @@ +package forge.game.event; + +import forge.game.Game; + +public class GameEventSubgameEnd extends GameEvent { + public final Game maingame; + public final String message; + + public GameEventSubgameEnd(Game game, String message0) { + maingame = game; + message = message0; + } + + @Override + public T visit(IGameEventVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/forge-game/src/main/java/forge/game/event/GameEventSubgameStart.java b/forge-game/src/main/java/forge/game/event/GameEventSubgameStart.java new file mode 100644 index 00000000000..674427a6f91 --- /dev/null +++ b/forge-game/src/main/java/forge/game/event/GameEventSubgameStart.java @@ -0,0 +1,19 @@ +package forge.game.event; + +import forge.game.Game; +import forge.game.card.Card; + +public class GameEventSubgameStart extends GameEvent { + public final Game subgame; + public final String message; + + public GameEventSubgameStart(Game subgame0, String message0) { + subgame = subgame0; + message = message0; + } + + @Override + public T visit(IGameEventVisitor visitor) { + return visitor.visit(this); + } +} diff --git a/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java b/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java index 2ded38b9cc4..0041e7545fc 100644 --- a/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java +++ b/forge-game/src/main/java/forge/game/event/IGameEventVisitor.java @@ -44,6 +44,8 @@ public interface IGameEventVisitor { T visit(GameEventSpellAbilityCast gameEventSpellAbilityCast); T visit(GameEventSpellResolved event); T visit(GameEventSpellRemovedFromStack event); + T visit(GameEventSubgameStart event); + T visit(GameEventSubgameEnd event); T visit(GameEventSurveil event); T visit(GameEventTokenCreated event); T visit(GameEventTurnBegan gameEventTurnBegan); @@ -92,6 +94,8 @@ public interface IGameEventVisitor { public T visit(GameEventSpellResolved event) { return null; } public T visit(GameEventSpellAbilityCast event) { return null; } public T visit(GameEventSpellRemovedFromStack event) { return null; } + public T visit(GameEventSubgameStart event) { return null; } + public T visit(GameEventSubgameEnd event) { return null; } public T visit(GameEventSurveil event) { return null; } public T visit(GameEventTokenCreated event) { return null; } public T visit(GameEventTurnBegan event) { return null; } diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 1ee63399db8..9c0995b0496 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -77,7 +77,7 @@ import java.util.concurrent.ConcurrentSkipListMap; public class Player extends GameEntity implements Comparable { public static final List ALL_ZONES = Collections.unmodifiableList(Arrays.asList(ZoneType.Battlefield, ZoneType.Library, ZoneType.Graveyard, ZoneType.Hand, ZoneType.Exile, ZoneType.Command, ZoneType.Ante, - ZoneType.Sideboard, ZoneType.PlanarDeck, ZoneType.SchemeDeck)); + ZoneType.Sideboard, ZoneType.PlanarDeck, ZoneType.SchemeDeck, ZoneType.Subgame)); private final Map commanderDamage = Maps.newHashMap(); @@ -141,6 +141,7 @@ public class Player extends GameEntity implements Comparable { private final Map zones = Maps.newEnumMap(ZoneType.class); private final Map adjustLandPlays = Maps.newHashMap(); private final Set adjustLandPlaysInfinite = Sets.newHashSet(); + private Map maingameCardsMap = Maps.newHashMap();; private CardCollection currentPlanes = new CardCollection(); private Set prowl = Sets.newHashSet(); @@ -1922,6 +1923,14 @@ public class Player extends GameEntity implements Comparable { return !adjustLandPlaysInfinite.isEmpty(); } + public final void addMaingameCardMapping(Card subgameCard, Card maingameCard) { + maingameCardsMap.put(subgameCard, maingameCard); + } + + public final Card getMappingMaingameCard(Card subgameCard) { + return maingameCardsMap.get(subgameCard); + } + public final ManaPool getManaPool() { return manaPool; } diff --git a/forge-game/src/main/java/forge/game/zone/ZoneType.java b/forge-game/src/main/java/forge/game/zone/ZoneType.java index 79ef9238ab9..d15e6daeb02 100644 --- a/forge-game/src/main/java/forge/game/zone/ZoneType.java +++ b/forge-game/src/main/java/forge/game/zone/ZoneType.java @@ -24,6 +24,7 @@ public enum ZoneType { Ante(false, "lblAnteZone"), SchemeDeck(true, "lblSchemeDeckZone"), PlanarDeck(true, "lblPlanarDeckZone"), + Subgame(true, "lblSubgameZone"), None(true, "lblNoneZone"); public static final List STATIC_ABILITIES_SOURCE_ZONES = Arrays.asList(Battlefield, Graveyard, Exile, Command/*, Hand*/); diff --git a/forge-gui/res/cardsfolder/s/shahrazad.txt b/forge-gui/res/cardsfolder/s/shahrazad.txt new file mode 100644 index 00000000000..f6de4101e23 --- /dev/null +++ b/forge-gui/res/cardsfolder/s/shahrazad.txt @@ -0,0 +1,10 @@ +Name:Shahrazad +ManaCost:W W +Types:Sorcery +A:SP$ Subgame | RememberPlayers$ NotWin | SubAbility$ DBRepeatEachPlayer | SpellDescription$ Players play a Magic subgame, using their libraries as their decks. Each player who doesn't win the subgame loses half their life, rounded up. | StackDescription$ SpellDescription +SVar:DBRepeatEachPlayer:DB$ RepeatEach | RepeatPlayers$ Remembered | ClearRememberedBeforeLoop$ True | RepeatSubAbility$ DBLoseLife | SpellDescription$ Each player who doesn't win the subgame loses half their life, rounded up. | StackDescription$ SpellDescription +SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ X | References$ X | Defined$ Player.IsRemembered +SVar:X:PlayerCountRemembered$LifeTotal/HalfUp +AI:RemoveDeck:All +AI:RemoveDeck:Random +Oracle:Players play a Magic subgame, using their libraries as their decks. Each player who doesn't win the subgame loses half their life, rounded up. diff --git a/forge-gui/res/languages/de-DE.properties b/forge-gui/res/languages/de-DE.properties index afa090b5adb..3534dfe9fea 100644 --- a/forge-gui/res/languages/de-DE.properties +++ b/forge-gui/res/languages/de-DE.properties @@ -1882,6 +1882,10 @@ lblDoYouWantSacrifice=Opfern durchführen? lblFaceDownCardCantTurnFaceUp=Verdeckte Karte kann nicht umgedreht werden #ShuffleEffect.java lblHaveTargetShuffle=Soll {0} mischen? +#SubgameEffect.java +lblSubgameStart=Subgame started by {0}''s effect. +lblSubgameEnd=Subgame ended. {0} wins. {1} loses. +lblSubgameEndDraw=Subgame ended in a draw. #SurveilEffect.java lblDoYouWantSurveil=Möchtest du Überwachen anwenden? #TapOrUntapAllEffect.java @@ -1937,6 +1941,7 @@ lblSideboardZone=Sideboard lblAnteZone=Ante lblSchemeDeckZone=Verschwörungsdeck lblPlanarDeckZone=Weltendeck +lblSubgameZone=subgame lblNoneZone=Keine #BoosterDraft.java lblChooseBlock=Wähle Block diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index 55547849226..c52e1f54380 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -1882,6 +1882,10 @@ lblDoYouWantSacrifice=Do you want to sacrifice? lblFaceDownCardCantTurnFaceUp=Face-down card can''t turn face up #ShuffleEffect.java lblHaveTargetShuffle=Have {0} shuffle? +#SubgameEffect.java +lblSubgameStart=Subgame started by {0}''s effect. +lblSubgameEnd=Subgame ended. {0} wins. {1} loses. +lblSubgameEndDraw=Subgame ended in a draw. #SurveilEffect.java lblDoYouWantSurveil=Do you want to surveil? #TapOrUntapAllEffect.java @@ -1937,6 +1941,7 @@ lblSideboardZone=sideboard lblAnteZone=ante lblSchemeDeckZone=schemedeck lblPlanarDeckZone=planardeck +lblSubgameZone=subgame lblNoneZone=none #BoosterDraft.java lblChooseBlock=Choose Block diff --git a/forge-gui/res/languages/es-ES.properties b/forge-gui/res/languages/es-ES.properties index 769e28dba5b..2b90de1b841 100644 --- a/forge-gui/res/languages/es-ES.properties +++ b/forge-gui/res/languages/es-ES.properties @@ -1882,6 +1882,10 @@ lblDoYouWantSacrifice=¿Quieres sacrificar? lblFaceDownCardCantTurnFaceUp=La carta boca abajo no se puede girar boca arriba #ShuffleEffect.java lblHaveTargetShuffle=¿Ha barajado {0}? +#SubgameEffect.java +lblSubgameStart=Subgame started by {0}''s effect. +lblSubgameEnd=Subgame ended. {0} wins. {1} loses. +lblSubgameEndDraw=Subgame ended in a draw. #SurveilEffect.java lblDoYouWantSurveil=¿Quieres vigilar? #TapOrUntapAllEffect.java @@ -1937,6 +1941,7 @@ lblSideboardZone=banquillo lblAnteZone=ante lblSchemeDeckZone=mazo scheme lblPlanarDeckZone=mazo planar +lblSubgameZone=subgame lblNoneZone=ninguna #BoosterDraft.java lblChooseBlock=Selecciona Bloque diff --git a/forge-gui/res/languages/it-IT.properties b/forge-gui/res/languages/it-IT.properties index 9dba07178e2..a13e803e8db 100644 --- a/forge-gui/res/languages/it-IT.properties +++ b/forge-gui/res/languages/it-IT.properties @@ -1882,6 +1882,10 @@ lblDoYouWantSacrifice=Do you want to sacrifice? lblFaceDownCardCantTurnFaceUp=Face-down card can''t turn face up #ShuffleEffect.java lblHaveTargetShuffle=Have {0} shuffle? +#SubgameEffect.java +lblSubgameStart=Subgame started by {0}''s effect. +lblSubgameEnd=Subgame ended. {0} wins. {1} loses. +lblSubgameEndDraw=Subgame ended in a draw. #SurveilEffect.java lblDoYouWantSurveil=Do you want to surveil? #TapOrUntapAllEffect.java @@ -1937,6 +1941,7 @@ lblSideboardZone=sideboard lblAnteZone=ante lblSchemeDeckZone=schemedeck lblPlanarDeckZone=planardeck +lblSubgameZone=subgame lblNoneZone=none #BoosterDraft.java lblChooseBlock=Choose Block diff --git a/forge-gui/res/languages/zh-CN.properties b/forge-gui/res/languages/zh-CN.properties index 5bd3630b8b2..164f9732f66 100644 --- a/forge-gui/res/languages/zh-CN.properties +++ b/forge-gui/res/languages/zh-CN.properties @@ -1882,6 +1882,10 @@ lblDoYouWantSacrifice=你想牺牲吗? lblFaceDownCardCantTurnFaceUp=面朝下的牌不能面朝上 #ShuffleEffect.java lblHaveTargetShuffle={0}洗牌了吗? +#SubgameEffect.java +lblSubgameStart=Subgame started by {0}''s effect. +lblSubgameEnd=Subgame ended. {0} wins. {1} loses. +lblSubgameEndDraw=Subgame ended in a draw. #SurveilEffect.java lblDoYouWantSurveil=你想刺探吗? #TapOrUntapAllEffect.java @@ -1937,6 +1941,7 @@ lblSideboardZone=备牌 lblAnteZone=赌注牌区 lblSchemeDeckZone=魔王套牌 lblPlanarDeckZone=时空套牌 +lblSubgameZone=subgame lblNoneZone=空 #BoosterDraft.java lblChooseBlock=选择环境 diff --git a/forge-gui/src/main/java/forge/match/HostedMatch.java b/forge-gui/src/main/java/forge/match/HostedMatch.java index 65b638a3131..7c66b7ea1d0 100644 --- a/forge-gui/src/main/java/forge/match/HostedMatch.java +++ b/forge-gui/src/main/java/forge/match/HostedMatch.java @@ -34,6 +34,7 @@ import forge.game.GameRules; import forge.game.GameType; import forge.game.GameView; import forge.game.Match; +import forge.game.event.*; import forge.game.player.Player; import forge.game.player.PlayerView; import forge.game.player.RegisteredPlayer; @@ -332,7 +333,7 @@ public class HostedMatch { return isMatchOver; } - private final class MatchUiEventVisitor implements IUiEventVisitor { + private final class MatchUiEventVisitor extends IGameEventVisitor.Base implements IUiEventVisitor { @Override public Void visit(final UiEventBlockerAssigned event) { for (final PlayerControllerHuman humanController : humanControllers) { @@ -359,6 +360,69 @@ public class HostedMatch { return null; } + @Override + public Void visit(final GameEventSubgameStart event) { + event.subgame.subscribeToEvents(SoundSystem.instance); + event.subgame.subscribeToEvents(visitor); + + final GameView gameView = event.subgame.getView(); + + Runnable switchGameView = new Runnable() { + @Override + public void run() { + for (final Player p : event.subgame.getPlayers()) { + if (p.getController() instanceof PlayerControllerHuman) { + final PlayerControllerHuman humanController = (PlayerControllerHuman) p.getController(); + final IGuiGame gui = guis.get(p.getRegisteredPlayer()); + humanController.setGui(gui); + gui.setGameView(null); + gui.setGameView(gameView); + gui.setOriginalGameController(p.getView(), humanController); + gui.openView(new TrackableCollection<>(p.getView())); + gui.setGameView(null); + gui.setGameView(gameView); + event.subgame.subscribeToEvents(new FControlGameEventHandler(humanController)); + gui.message(event.message); + } + } + } + }; + GuiBase.getInterface().invokeInEdtAndWait(switchGameView); + + //ensure opponents set properly + for (final Player p : event.subgame.getPlayers()) { + p.updateOpponentsForView(); + } + + return null; + } + + @Override + public Void visit(final GameEventSubgameEnd event) { + final GameView gameView = event.maingame.getView(); + Runnable switchGameView = new Runnable() { + @Override + public void run() { + for (final Player p : event.maingame.getPlayers()) { + if (p.getController() instanceof PlayerControllerHuman) { + final PlayerControllerHuman humanController = (PlayerControllerHuman) p.getController(); + final IGuiGame gui = guis.get(p.getRegisteredPlayer()); + gui.setGameView(null); + gui.setGameView(gameView); + gui.setOriginalGameController(p.getView(), humanController); + gui.openView(new TrackableCollection<>(p.getView())); + gui.setGameView(null); + gui.setGameView(gameView); + gui.updatePhase(); + gui.message(event.message); + } + } + } + }; + GuiBase.getInterface().invokeInEdtAndWait(switchGameView); + return null; + } + @Subscribe public void receiveEvent(final UiEvent evt) { try { @@ -368,6 +432,16 @@ public class HostedMatch { e.printStackTrace(); } } + + @Subscribe + public void receiveGameEvent(final GameEvent evt) { + try { + evt.visit(this); + } catch (Exception e) { + System.out.println(e.getMessage()); + e.printStackTrace(); + } + } } private void addNextGameDecision(final PlayerControllerHuman controller, final NextGameDecision decision) {