diff --git a/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java b/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java index 3b38efabd61..771ad61eac8 100644 --- a/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java +++ b/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java @@ -12,6 +12,7 @@ import forge.screens.home.gauntlet.VSubmenuGauntletQuick; import forge.screens.home.online.VSubmenuOnlineLobby; import forge.screens.home.puzzle.VSubmenuPuzzleCreate; import forge.screens.home.puzzle.VSubmenuPuzzleSolve; +import forge.screens.home.puzzle.VSubmenuTutorial; import forge.screens.home.quest.*; import forge.screens.home.sanctioned.VSubmenuConstructed; import forge.screens.home.sanctioned.VSubmenuDraft; @@ -63,6 +64,7 @@ public enum EDocID { HOME_ACHIEVEMENTS (VSubmenuAchievements.SINGLETON_INSTANCE), HOME_AVATARS (VSubmenuAvatars.SINGLETON_INSTANCE), HOME_UTILITIES (VSubmenuDownloaders.SINGLETON_INSTANCE), + HOME_TUTORIAL(VSubmenuTutorial.SINGLETON_INSTANCE), HOME_PUZZLE_CREATE(VSubmenuPuzzleCreate.SINGLETON_INSTANCE), HOME_PUZZLE_SOLVE(VSubmenuPuzzleSolve.SINGLETON_INSTANCE), HOME_CONSTRUCTED (VSubmenuConstructed.SINGLETON_INSTANCE), diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/VHomeUI.java b/forge-gui-desktop/src/main/java/forge/screens/home/VHomeUI.java index 7bbe5b2d7d6..fe610e80de0 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/VHomeUI.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/VHomeUI.java @@ -29,6 +29,7 @@ import forge.screens.home.gauntlet.VSubmenuGauntletQuick; import forge.screens.home.online.VSubmenuOnlineLobby; import forge.screens.home.puzzle.VSubmenuPuzzleCreate; import forge.screens.home.puzzle.VSubmenuPuzzleSolve; +import forge.screens.home.puzzle.VSubmenuTutorial; import forge.screens.home.quest.*; import forge.screens.home.sanctioned.VSubmenuConstructed; import forge.screens.home.sanctioned.VSubmenuDraft; @@ -44,8 +45,8 @@ import net.miginfocom.swing.MigLayout; import javax.swing.*; import java.awt.*; -import java.util.List; import java.util.*; +import java.util.List; /** * Top level view class for home UI drag layout.
@@ -114,6 +115,7 @@ public enum VHomeUI implements IVTopLevelUI { allSubmenus.add(VSubmenuPuzzleSolve.SINGLETON_INSTANCE); allSubmenus.add(VSubmenuPuzzleCreate.SINGLETON_INSTANCE); + allSubmenus.add(VSubmenuTutorial.SINGLETON_INSTANCE); allSubmenus.add(VSubmenuPreferences.SINGLETON_INSTANCE); allSubmenus.add(VSubmenuAchievements.SINGLETON_INSTANCE); 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 eee3e0e482e..ddfb8583b0d 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 @@ -13,10 +13,11 @@ import forge.match.HostedMatch; import forge.menus.IMenuProvider; import forge.menus.MenuUtil; import forge.player.GamePlayerUtil; +import forge.properties.ForgeConstants; import forge.puzzle.Puzzle; import forge.puzzle.PuzzleIO; -import forge.util.gui.SOptionPane; import forge.util.Localizer; +import forge.util.gui.SOptionPane; import javax.swing.*; import java.awt.event.ActionEvent; @@ -53,7 +54,7 @@ public enum CSubmenuPuzzleSolve implements ICDoc, IMenuProvider { }; private void updateData() { - final ArrayList puzzles = PuzzleIO.loadPuzzles(); + final ArrayList puzzles = PuzzleIO.loadPuzzles(ForgeConstants.PUZZLE_DIR); Collections.sort(puzzles); for(Puzzle p : puzzles) { 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 new file mode 100644 index 00000000000..9ef4597861e --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/CSubmenuTutorial.java @@ -0,0 +1,130 @@ +package forge.screens.home.puzzle; + +import forge.GuiBase; +import forge.UiCommand; +import forge.assets.FSkinProp; +import forge.deck.Deck; +import forge.game.GameRules; +import forge.game.GameType; +import forge.game.player.RegisteredPlayer; +import forge.gui.SOverlayUtils; +import forge.gui.framework.ICDoc; +import forge.match.HostedMatch; +import forge.menus.IMenuProvider; +import forge.menus.MenuUtil; +import forge.player.GamePlayerUtil; +import forge.properties.ForgeConstants; +import forge.puzzle.Puzzle; +import forge.puzzle.PuzzleIO; +import forge.util.Localizer; +import forge.util.gui.SOptionPane; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public enum CSubmenuTutorial implements ICDoc, IMenuProvider { + SINGLETON_INSTANCE; + + private VSubmenuTutorial view = VSubmenuTutorial.SINGLETON_INSTANCE; + + @Override + public void register() { + + } + + @Override + public void initialize() { + view.getList().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + updateData(); + view.getBtnStart().addActionListener( + new ActionListener() { @Override + public void actionPerformed(final ActionEvent e) { startPuzzleSolve(); } }); + } + + private final UiCommand cmdStart = new UiCommand() { + private static final long serialVersionUID = -367368436333443417L; + + @Override public void run() { + startPuzzleSolve(); + } + }; + + private void updateData() { + final ArrayList tutorials = PuzzleIO.loadPuzzles(ForgeConstants.TUTORIAL_DIR); + Collections.sort(tutorials); + + for(Puzzle p : tutorials) { + view.getModel().addElement(p); + } + } + + @Override + public void update() { + MenuUtil.setMenuProvider(this); + } + + @Override + public List getMenus() { + final List menus = new ArrayList<>(); + menus.add(PuzzleGameMenu.getMenu()); + return menus; + } + + private boolean startPuzzleSolve() { + final Puzzle selected = (Puzzle)view.getList().getSelectedValue(); + if (selected == null) { + SOptionPane.showMessageDialog(Localizer.getInstance().getMessage("lblPleaseFirstSelectAPuzzleFromList"), Localizer.getInstance().getMessage("lblNoSelectedPuzzle"), FSkinProp.ICO_ERROR); + return false; + } + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + SOverlayUtils.startGameOverlay(); + SOverlayUtils.showOverlay(); + } + }); + + final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch(); + hostedMatch.setStartGameHook(new Runnable() { + @Override + public final void run() { + SOptionPane.showMessageDialog(selected.getGoalDescription(), selected.getName(), SOptionPane.INFORMATION_ICON); + selected.applyToGame(hostedMatch.getGame()); + } + }); + + hostedMatch.setEndGameHook((new Runnable() { + @Override + public void run() { + selected.savePuzzleSolve(hostedMatch.getGame().getOutcome().isWinner(GamePlayerUtil.getGuiPlayer())); + } + })); + + final List players = new ArrayList<>(); + final RegisteredPlayer human = new RegisteredPlayer(new Deck()).setPlayer(GamePlayerUtil.getGuiPlayer()); + human.setStartingHand(0); + players.add(human); + + final RegisteredPlayer ai = new RegisteredPlayer(new Deck()).setPlayer(GamePlayerUtil.createAiPlayer()); + ai.setStartingHand(0); + players.add(ai); + + GameRules rules = new GameRules(GameType.Puzzle); + rules.setGamesPerMatch(1); + hostedMatch.startMatch(rules, null, players, human, GuiBase.getInterface().getNewGuiGame()); + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + SOverlayUtils.hideOverlay(); + } + }); + + return true; + } +} diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/VSubmenuTutorial.java b/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/VSubmenuTutorial.java new file mode 100644 index 00000000000..fc795c02bc3 --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/screens/home/puzzle/VSubmenuTutorial.java @@ -0,0 +1,122 @@ +package forge.screens.home.puzzle; + +import forge.gui.framework.DragCell; +import forge.gui.framework.DragTab; +import forge.gui.framework.EDocID; +import forge.interfaces.IPlayerChangeListener; +import forge.match.GameLobby; +import forge.match.LocalLobby; +import forge.net.event.UpdateLobbyPlayerEvent; +import forge.screens.home.*; +import forge.toolbox.FList; +import forge.toolbox.FScrollPane; +import forge.util.Localizer; +import net.miginfocom.swing.MigLayout; + +import javax.swing.*; + +public enum VSubmenuTutorial implements IVSubmenu { + SINGLETON_INSTANCE; + + private final FList tutorialList; + private final FScrollPane tutorialListPane; + + final DefaultListModel model = new DefaultListModel(); + + private final StartButton btnStart = new StartButton(); + + private DragCell parentCell; + final Localizer localizer = Localizer.getInstance(); + private final DragTab tab = new DragTab(localizer.getMessage("lblTutorialMode")); + + private final GameLobby lobby = new LocalLobby(); + private final VLobby vLobby = new VLobby(lobby); + + VSubmenuTutorial() { + tutorialList = new FList<>(); + tutorialListPane = new FScrollPane(this.tutorialList, true); + + lobby.setListener(vLobby); + + vLobby.setPlayerChangeListener(new IPlayerChangeListener() { + @Override public final void update(final int index, final UpdateLobbyPlayerEvent event) { + lobby.applyToSlot(index, event); + } + }); + + vLobby.update(false); + } + + @Override + public EMenuGroup getGroupEnum() { + return EMenuGroup.PUZZLE; + } + + @Override + public String getMenuTitle() { + final Localizer localizer = Localizer.getInstance(); + return localizer.getMessage("lblTutorial"); + } + + @Override + public EDocID getItemEnum() { + return EDocID.HOME_TUTORIAL; + } + + @Override + public EDocID getDocumentID() { + return EDocID.HOME_TUTORIAL; + } + + @Override + public DragTab getTabLabel() { + return tab; + } + + @Override + public CSubmenuTutorial getLayoutControl() { + return CSubmenuTutorial.SINGLETON_INSTANCE; + } + + @Override + public void setParentCell(DragCell cell0) { + this.parentCell = cell0; + } + + @Override + public DragCell getParentCell() { + return this.parentCell; + } + + public JList getList() { + return tutorialList; + } + + public DefaultListModel getModel() { + return model; + } + + public StartButton getBtnStart() { + return btnStart; + } + + @Override + public void populate() { + final JPanel container = VHomeUI.SINGLETON_INSTANCE.getPnlDisplay(); + + container.removeAll(); + container.setLayout(new MigLayout("insets 0, gap 0, wrap 1, ax right")); + final Localizer localizer = Localizer.getInstance(); + vLobby.getLblTitle().setText(localizer.getMessage("lblTutorialMode")); + container.add(vLobby.getLblTitle(), "w 80%, h 40px!, gap 0 0 15px 15px, span 2, al right, pushx"); + tutorialList.setModel(model); + container.add(tutorialListPane, "w 80%, h 80%, gap 0 0 0px 0px, span 2, al center"); + container.add(btnStart, "w 98%!, ax center, gap 1% 0 20px 20px, span 2"); + + + if (container.isShowing()) { + container.validate(); + container.repaint(); + } + } +} diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index 7d1ef1cb8d7..8346e089c24 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -570,6 +570,9 @@ lblRandomThemeDecks=Random Theme Decks lblRandomDecks=Random Decks lblNetDecks=Net Decks lblNetCommanderDecks=Net Commander Decks +#VSubmenuTutorial +lblTutorial=Tutorial +lblTutorialMode=Tutorial Mode #VSubmenuPuzzleSolve.java lblSolve=Solve lblPuzzleModeSolve=Puzzle Mode: Solve diff --git a/forge-gui/res/tutorial/Spellslinger.pzl b/forge-gui/res/tutorial/Spellslinger.pzl new file mode 100644 index 00000000000..f57eb7fa761 --- /dev/null +++ b/forge-gui/res/tutorial/Spellslinger.pzl @@ -0,0 +1,25 @@ +[metadata] +Name:Spellslinger WR vs BG (Win the Game) +URL:https://www.cardforge.org +Goal:Win +Turns:16 +Difficulty:Easy +Description:This is a preconfigured game, where you need to overcome your opponents defenses by any means necessary and to reduce his/her lifepoints to 0 before you both run out of cards in your respective libraries. There are several ways to reach your goal. +[state] +humanlife=10 +ailife=10 +turn=1 +activeplayer=human +activephase=MAIN1 +humanhand=Plains;Plains;Mountain;Mountain;Silverbeak Griffin;Hostile Minotaur;Charging Monstrosaur +humanlibrary=Inspiring Captain;Knight of New Benalia;Frenzied Raptor;Moment of Triumph;Luminous Bonds;Plains;Plains +humangraveyard= +humanbattlefield= +humanexile= +humancommand= +aihand=Swamp;Swamp;Forest;Forest;Colossal Dreadmaw;Hitchclaw Recluse;Walking Corpse +ailibrary=Impale;Ravenous Chupacabra;Swamp;Titanic Growth;Forest;Vampire Sovereign;Greenwood Sentinel +aigraveyard= +aibattlefield= +aiexile= +aicommand= \ No newline at end of file diff --git a/forge-gui/src/main/java/forge/properties/ForgeConstants.java b/forge-gui/src/main/java/forge/properties/ForgeConstants.java index 12e9b309a5d..8cd08f0052c 100644 --- a/forge-gui/src/main/java/forge/properties/ForgeConstants.java +++ b/forge-gui/src/main/java/forge/properties/ForgeConstants.java @@ -69,6 +69,7 @@ public final class ForgeConstants { public static final String LANG_DIR = RES_DIR + "languages" + PATH_SEPARATOR; public static final String EFFECTS_DIR = RES_DIR + "effects" + PATH_SEPARATOR; public static final String PUZZLE_DIR = RES_DIR + "puzzle" + PATH_SEPARATOR; + public static final String TUTORIAL_DIR = RES_DIR + "tutorial" + PATH_SEPARATOR; public static final String DECK_GEN_DIR = RES_DIR + "deckgendecks" + PATH_SEPARATOR; diff --git a/forge-gui/src/main/java/forge/puzzle/PuzzleIO.java b/forge-gui/src/main/java/forge/puzzle/PuzzleIO.java index ddec7e8f2a2..2f230c0c808 100644 --- a/forge-gui/src/main/java/forge/puzzle/PuzzleIO.java +++ b/forge-gui/src/main/java/forge/puzzle/PuzzleIO.java @@ -16,10 +16,10 @@ public class PuzzleIO { public static final String SUFFIX_DATA = ".pzl"; public static final String SUFFIX_COMPLETE = ".complete"; - public static ArrayList loadPuzzles() { + public static ArrayList loadPuzzles(String directory) { String[] pList; // get list of puzzles - final File pFolder = new File(ForgeConstants.PUZZLE_DIR); + final File pFolder = new File(directory); if (!pFolder.exists()) { throw new RuntimeException("Puzzles : folder not found -- folder is " + pFolder.getAbsolutePath()); } @@ -33,7 +33,7 @@ public class PuzzleIO { ArrayList puzzles = Lists.newArrayList(); for (final String element : pList) { if (element.endsWith(SUFFIX_DATA)) { - final List pfData = FileUtil.readFile(ForgeConstants.PUZZLE_DIR + element); + final List pfData = FileUtil.readFile(directory + element); String filename = element.replace(SUFFIX_DATA, ""); boolean completed = FileUtil.doesFileExist(ForgeConstants.USER_PUZZLE_DIR + element.replace(SUFFIX_DATA, SUFFIX_COMPLETE)); @@ -48,9 +48,4 @@ public class PuzzleIO { public static Map> parsePuzzleSections(List pfData) { return FileSection.parseSections(pfData); } - - - public static File getPuzzleFile(final String name) { - return new File(ForgeConstants.PUZZLE_DIR, name + SUFFIX_DATA); - } }