diff --git a/forge-game/src/main/java/forge/game/GameState.java b/forge-game/src/main/java/forge/game/GameState.java index 59b96fa9b87a..11bdff2456dc 100644 --- a/forge-game/src/main/java/forge/game/GameState.java +++ b/forge-game/src/main/java/forge/game/GameState.java @@ -66,6 +66,7 @@ static class PlayerState { private String precast = null; private String putOnStack = null; private final Map cardTexts = new EnumMap<>(ZoneType.class); + private String name = null; } private final List playerStates = new ArrayList<>(); @@ -574,6 +575,8 @@ else if (categoryName.endsWith("phaseadvance")) getPlayerState(categoryName).manaPool = categoryValue; } else if (categoryName.endsWith("persistentmana")) { getPlayerState(categoryName).persistentMana = categoryValue; + } else if (categoryName.endsWith("name")) { + getPlayerState(categoryName).name = categoryValue; } else { System.err.println("Unknown key: " + categoryName); } @@ -1136,6 +1139,9 @@ private void applyCountersToGameEntity(GameEntity entity, String counterString) private void setupPlayerState(final Player p, final PlayerState state) { // Lock check static as we setup player state + if (state.name != null && !state.name.isEmpty()) { + p.setName(state.name); + } // Clear all zones first, this ensures that any lingering cards and effects (e.g. in command zone) get cleared up // before setting up a new state diff --git a/forge-game/src/main/java/forge/game/GameView.java b/forge-game/src/main/java/forge/game/GameView.java index 3a699a7008e3..d0578f3cb7f9 100644 --- a/forge-game/src/main/java/forge/game/GameView.java +++ b/forge-game/src/main/java/forge/game/GameView.java @@ -72,6 +72,9 @@ public void updatePlayers(final Game game) { public String getTitle() { return get(TrackableProperty.Title); } + public void updateTitle() { + set(TrackableProperty.Title, match.getTitle()); + } public boolean isCommander() { return get(TrackableProperty.IsCommander); diff --git a/forge-game/src/main/java/forge/game/Match.java b/forge-game/src/main/java/forge/game/Match.java index dc9efb22da71..24c676582cdf 100644 --- a/forge-game/src/main/java/forge/game/Match.java +++ b/forge-game/src/main/java/forge/game/Match.java @@ -33,7 +33,7 @@ public class Match { private List removedCards = Lists.newArrayList(); private final List players; private final GameRules rules; - private final String title; + private String title; private final EventBus events = new EventBus("match events"); private final Map gameOutcomes = Maps.newHashMap(); @@ -49,6 +49,9 @@ public Match(final GameRules rules0, final List players0, fina public GameRules getRules() { return rules; } + public void setTitle(final String title) { + this.title = title; + } String getTitle() { final Multiset wins = getGamesWon(); final StringBuilder titleAppend = new StringBuilder(title); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuPuzzleCreate.java b/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuPuzzleCreate.java index aed0f3336d07..256a438b3026 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuPuzzleCreate.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuPuzzleCreate.java @@ -93,6 +93,7 @@ private void startPuzzleCreate() { SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblWelcomePuzzleModeMessage"), Localizer.getInstance().getMessage("lblCreateNewPuzzle"), SOptionPane.WARNING_ICON); emptyPuzzle.applyToGame(hostedMatch.getGame()); + hostedMatch.refreshTitle(); }); final List players = new ArrayList<>(); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuPuzzleSolve.java b/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuPuzzleSolve.java index c9a9f387a98f..5919e295b621 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuPuzzleSolve.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuPuzzleSolve.java @@ -89,6 +89,7 @@ private boolean startPuzzleSolve() { hostedMatch.setStartGameHook(() -> { SOptionPane.showMessageDialog(selected.getGoalDescription(), selected.getName(), SOptionPane.INFORMATION_ICON); selected.applyToGame(hostedMatch.getGame()); + hostedMatch.refreshTitle(); }); hostedMatch.setEndGameHook((() -> selected.savePuzzleSolve(hostedMatch.getGame().getOutcome().isWinner(GamePlayerUtil.getGuiPlayer())))); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuTutorial.java b/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuTutorial.java index 3fd9e3fabfb4..6b6ec36761ff 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuTutorial.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuTutorial.java @@ -89,6 +89,7 @@ private boolean startPuzzleSolve() { hostedMatch.setStartGameHook(() -> { SOptionPane.showMessageDialog(selected.getGoalDescription(), selected.getName(), SOptionPane.INFORMATION_ICON); selected.applyToGame(hostedMatch.getGame()); + hostedMatch.refreshTitle(); }); hostedMatch.setEndGameHook((() -> selected.savePuzzleSolve(hostedMatch.getGame().getOutcome().isWinner(GamePlayerUtil.getGuiPlayer())))); diff --git a/forge-gui-mobile/src/forge/screens/home/puzzle/PuzzleScreen.java b/forge-gui-mobile/src/forge/screens/home/puzzle/PuzzleScreen.java index e318b0b1c922..ab6e00e83e87 100644 --- a/forge-gui-mobile/src/forge/screens/home/puzzle/PuzzleScreen.java +++ b/forge-gui-mobile/src/forge/screens/home/puzzle/PuzzleScreen.java @@ -60,7 +60,10 @@ protected void startMatch() { LoadingOverlay.show(Forge.getLocalizer().getMessage("lblLoadingThePuzzle"), true, () -> { // Load selected puzzle final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch(); - hostedMatch.setStartGameHook(() -> chosen.applyToGame(hostedMatch.getGame())); + hostedMatch.setStartGameHook(() -> { + chosen.applyToGame(hostedMatch.getGame()); + hostedMatch.refreshTitle(); + }); hostedMatch.setEndGameHook((() -> chosen.savePuzzleSolve(hostedMatch.getGame().getOutcome().isWinner(GamePlayerUtil.getGuiPlayer())))); diff --git a/forge-gui/res/puzzle/Duelist_Number_5.pzl b/forge-gui/res/puzzle/Duelist_Number_5.pzl new file mode 100644 index 000000000000..446dc08050b9 --- /dev/null +++ b/forge-gui/res/puzzle/Duelist_Number_5.pzl @@ -0,0 +1,22 @@ +[metadata] +Name:Magic the Puzzling - Duelist #5 - Eeny Meeny Miney Moe +Goal:Win +Turns:1 +Difficulty:Common +Description:Your opponent is Nick. He's under the false assumption that he's going to win this duel. Of course, with Circles of Protection for two of your three colors, four blockers, and a Conversion in play, he has plenty of reason to be cocky. But then, you draw an Instill Energy and the solution comes to you. You're going to take Nick down this turn and you plan to enjoy the look on his face when you do it.\n\nIt is the beginning of your upkeep phase. Defeat Nick this turn without dying. +[state] +ActivePlayer=human +ActivePhase=upkeep +HumanLife=5 +AILife=12 +AIName=Nick +humanhand=Black Ward|Set:3ED; Disenchant|Set:3ED; Earthbind|Set:3ED; Swords to Plowshares|Set:3ED; Instill Energy|Set:3ED; Flashfires|Set:3ED; Red Elemental Blast|Set:3ED; Stone Rain|Set:3ED; Giant Growth|Set:3ED; Black Knight|Set:3ED; +humanprecast= +humangraveyard= +humanlibrary= +humanBattlefield=Plains|Set:3ED; Plains|Set:3ED; Plains|Set:3ED; Plains|Set:3ED; Forest|Set:3ED; Forest|Set:3ED; Mountain|Set:3ED; Mountain|Set:3ED; Mountain|Set:3ED; Mountain|Set:3ED; Basalt Monolith|Set:3ED; Sol Ring|Set:3ED; Rod of Ruin|Set:3ED; Stone Giant|Id:1|Set:3ED; Dwarven Weaponsmith|Set:3ED; Grizzly Bears|Set:3ED; Hill Giant|Set:3ED; Library of Leng|Set:3ED; +aibattlefield=Plains|Set:3ED; Plains|Set:3ED; Swamp|Tapped|Set:3ED; Swamp|Tapped|Set:3ED; Swamp|Tapped|Set:3ED; Island|Tapped|Set:3ED; Island|Tapped|Set:3ED; Sol Ring|Tapped|Set:3ED; Circle of Protection: Red|Set:3ED; Circle of Protection: Green|Set:3ED; Phantasmal Forces|Set:3ED; Hypnotic Specter|Counters:M1M1=3|Id:11|Set:3ED; Unstable Mutation|AttachedTo:11|Set:3ED; Pearled Unicorn|Id:10|Set:3ED; Flight|AttachedTo:10|Set:3ED; Wall of Water|Set:3ED; Conversion|Set:3ED; Dingus Egg|Set:3ED;Weakness|AttachedTo:1|Set:3ED; +aiprecast= +aigraveyard= +ailibrary= +aiexile= diff --git a/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java b/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java index 2fffd23b1168..a73d67827475 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java @@ -366,6 +366,33 @@ public GameView getGameView() { return game == null ? null : game.getView(); } + // Recomputes the "X vs Y" match title from the players' current in-game names + // and re-pushes it to any open guis. Needed after a puzzle renames a player + // (e.g. via AIName) since the title is normally captured once at match start, + // before the puzzle's GameState has been applied. + public void refreshTitle() { + if (game == null || match == null) { + return; + } + final FCollectionView players = game.getPlayers(); + if (players.size() != 2) { + return; + } + match.setTitle(TextUtil.concatNoSpace(players.get(0).getName(), " vs ", players.get(1).getName())); + + final GameView gameView = getGameView(); + if (gameView == null) { + return; + } + gameView.updateTitle(); + + FThreads.invokeInEdtNowOrLater(() -> { + for (final IGuiGame gui : guis.values()) { + gui.setGameView(gameView); + } + }); + } + public void endCurrentGame() { if (game == null) { return; } boolean isMatchOver = game.getView().isMatchOver(); diff --git a/forge-gui/src/main/java/forge/gamemodes/puzzle/Puzzle.java b/forge-gui/src/main/java/forge/gamemodes/puzzle/Puzzle.java index 9916be959ba0..60693fa671b6 100644 --- a/forge-gui/src/main/java/forge/gamemodes/puzzle/Puzzle.java +++ b/forge-gui/src/main/java/forge/gamemodes/puzzle/Puzzle.java @@ -29,6 +29,7 @@ public class Puzzle extends GameState implements InventoryItem, Comparable metadataLines) { this.difficulty = split[1].trim(); } else if ("Description".equalsIgnoreCase(split[0])) { this.description = split[1].trim(); + } else if ("AIName".equalsIgnoreCase(split[0])) { + this.aiName = split[1].trim(); } else if ("Targets".equalsIgnoreCase(split[0])) { this.targets = split[1].trim(); } else if ("TargetCount".equalsIgnoreCase(split[0])) { @@ -94,6 +97,11 @@ public String getGoalDescription() { desc.append("\nTurns Limit: "); desc.append(this.turns); + if (this.aiName != null) { + desc.append("\nOpponent: "); + desc.append(this.aiName); + } + if (this.description != null) { desc.append("\n\n"); desc.append(this.description.replace("\\n", "\n")); @@ -247,6 +255,8 @@ public String getName() { public boolean getCompleted() { return completed; } + public String getAIName() { return aiName; } + public String toString() { StringBuilder sb = new StringBuilder(); if (this.completed) {