diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/PathsSingleton.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/PathsSingleton.java index 2353206c4c9b598f4b5ebd3b6c012207d372c682..1c98afb3100fed235f8265f4132a6f4918cab934 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/PathsSingleton.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/PathsSingleton.java @@ -3,13 +3,18 @@ package edu.ntnu.idatt2001.group_30.paths; import edu.ntnu.idatt2001.group_30.paths.model.Player; import edu.ntnu.idatt2001.group_30.paths.model.Story; import edu.ntnu.idatt2001.group_30.paths.model.goals.*; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javafx.scene.image.ImageView; /** * This enumeration is constructed using the singleton design pattern. The implementation of this design pattern * restricts the amount of instances of the enumeration to one. This is essential for the enumeration's purpose. * The enum contains all the data that needs to be transmitted between controllers. * - * @author Trym Hamer Gudvangen + * @author Trym Hamer Gudvangen, Nicolai H. Brand. */ public enum PathsSingleton { INSTANCE; @@ -21,6 +26,7 @@ public enum PathsSingleton { private ScoreGoal scoreGoal; private InventoryGoal inventoryGoal; private GoldGoal goldGoal; + private ImageView characterImageView; /** * This method gets the current selected story. @@ -85,6 +91,30 @@ public enum PathsSingleton { }; } + public ImageView getCharacterImageView() { + return characterImageView; + } + + public void setCharacterImageView(ImageView characterImageView) { + this.characterImageView = characterImageView; + } + + /** + * Returns a list of all the non-null goals. + * @return A list of all the non-null goals, given as a List of Goal objects. + */ + public List<Goal> getGoals() { + List<Goal> goals = Stream + .of(healthGoal, scoreGoal, goldGoal) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (inventoryGoal != null && inventoryGoal.getGoalValue().size() != 0) { + goals.add(inventoryGoal); + } + return goals; + } + /** * This method checks whether a passage is currently being moved. * @return The passageMoving status, given as a boolean. {@code true} if the passage is moving, else {@code false} diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/Controller.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/Controller.java index 1e029ed52c20f9e1d7ce3009e74ae45ed46e444a..4a62d0acd5453a9fbbda8ba8aa3494761a393c77 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/Controller.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/Controller.java @@ -1,8 +1,8 @@ package edu.ntnu.idatt2001.group_30.paths.controller; -import edu.ntnu.idatt2001.group_30.paths.view.HomeView; -import edu.ntnu.idatt2001.group_30.paths.view.View; -import edu.ntnu.idatt2001.group_30.paths.view.ViewFactory; +import edu.ntnu.idatt2001.group_30.paths.view.views.HomeView; +import edu.ntnu.idatt2001.group_30.paths.view.views.View; +import edu.ntnu.idatt2001.group_30.paths.view.views.ViewFactory; import java.util.HashMap; import java.util.Map; import javafx.event.ActionEvent; @@ -44,6 +44,15 @@ public class Controller { return actionEvent -> STAGE_MANAGER.goBack(); } + /** + * Go back to the previous instance of the given view class. + * @param viewClass The class of the view to go back to. + * @return An EventHandler that will set the current view to the previous instance of the given view class. + */ + public EventHandler<ActionEvent> goBackTo(Class<? extends View<?>> viewClass) { + return actionEvent -> STAGE_MANAGER.goBackTo(viewClass); + } + public Stage getRootStage() { return STAGE_MANAGER.getStage(); } diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/CreatePlayerController.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/CreatePlayerController.java index 8e6c81015ffcc249e53a8baba76d8f56d194e22f..01c52afc3b17a81c271d52b77ff23a99784b43b8 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/CreatePlayerController.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/CreatePlayerController.java @@ -1,6 +1,6 @@ package edu.ntnu.idatt2001.group_30.paths.controller; -import edu.ntnu.idatt2001.group_30.paths.view.NewGameView; +import edu.ntnu.idatt2001.group_30.paths.view.views.NewGameView; public class CreatePlayerController extends Controller { diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/HelpController.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/HelpController.java index 9f594963bd4823022f220c861c2d1bc47f0ea06a..71d54a3c1d9e2db538ed3100a82b83a00b10278d 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/HelpController.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/HelpController.java @@ -1,6 +1,6 @@ package edu.ntnu.idatt2001.group_30.paths.controller; -import edu.ntnu.idatt2001.group_30.paths.view.HomeView; +import edu.ntnu.idatt2001.group_30.paths.view.views.HomeView; /** * This class is used to control the HelpView. diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/HomeController.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/HomeController.java index 95eaa202e3a97917e4aa68e5040309827aa6f612..d3090acb250e365cfea8c71a85cb03b675af5d05 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/HomeController.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/HomeController.java @@ -1,9 +1,9 @@ package edu.ntnu.idatt2001.group_30.paths.controller; -import edu.ntnu.idatt2001.group_30.paths.view.CreatePlayerView; -import edu.ntnu.idatt2001.group_30.paths.view.HelpView; -import edu.ntnu.idatt2001.group_30.paths.view.NewGameView; -import edu.ntnu.idatt2001.group_30.paths.view.PlaythroughView; +import edu.ntnu.idatt2001.group_30.paths.view.views.CreatePlayerView; +import edu.ntnu.idatt2001.group_30.paths.view.views.HelpView; +import edu.ntnu.idatt2001.group_30.paths.view.views.NewGameView; +import edu.ntnu.idatt2001.group_30.paths.view.views.PlaythroughView; /** * This class is used to control the HomeView. diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/NewGameController.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/NewGameController.java index 0cf4ad1e06f3002f3e7a0923c0d00f4d5f0bdfac..9757fb6726b17dde18054c590e22a1f6b97a7cbe 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/NewGameController.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/NewGameController.java @@ -3,7 +3,7 @@ package edu.ntnu.idatt2001.group_30.paths.controller; import static edu.ntnu.idatt2001.group_30.paths.PathsSingleton.INSTANCE; import edu.ntnu.idatt2001.group_30.paths.model.filehandling.StoryFileHandler; -import edu.ntnu.idatt2001.group_30.paths.view.PlaythroughView; +import edu.ntnu.idatt2001.group_30.paths.view.views.PlaythroughView; import java.io.File; import java.io.IOException; diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/PlaytroughController.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/PlaytroughController.java index bff6bbfd6e36d2a890ebe489bf442eaa6bd58c07..ca59f34f0f88da7c8a0fa7b6b934001d4c70b089 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/PlaytroughController.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/PlaytroughController.java @@ -4,16 +4,16 @@ import static edu.ntnu.idatt2001.group_30.paths.PathsSingleton.INSTANCE; import edu.ntnu.idatt2001.group_30.paths.model.*; import edu.ntnu.idatt2001.group_30.paths.model.goals.Goal; -import edu.ntnu.idatt2001.group_30.paths.model.goals.HealthGoal; -import edu.ntnu.idatt2001.group_30.paths.model.goals.ScoreGoal; -import edu.ntnu.idatt2001.group_30.paths.view.HelpView; -import edu.ntnu.idatt2001.group_30.paths.view.HomeView; +import edu.ntnu.idatt2001.group_30.paths.view.views.HelpView; +import edu.ntnu.idatt2001.group_30.paths.view.views.HomeView; import java.util.ArrayList; import java.util.HashMap; +import java.util.Objects; import javafx.beans.property.*; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.ObservableMap; +import javafx.scene.image.ImageView; /** * Controller for the play-through of the game. @@ -26,6 +26,7 @@ public class PlaytroughController extends Controller { private Game game; /* reactive state */ + private final StringProperty gameTitle = new SimpleStringProperty("Playing: " + INSTANCE.getStory().getTitle()); private final StringProperty passageTitle = new SimpleStringProperty(); private final StringProperty passageContent = new SimpleStringProperty(); private final ObservableList<Link> links = FXCollections.observableList(new ArrayList<>()); @@ -52,16 +53,17 @@ public class PlaytroughController extends Controller { * It also resets the reactive properties. */ public void startNewGame() { - //TODO: add from Singleton - Player player = new Player("Gorm", 50, 0, 100); - HealthGoal healthGoal = new HealthGoal(10); - ScoreGoal scoreGoal = new ScoreGoal(100); - ArrayList<Goal> goals = new ArrayList<>(); - goals.add(healthGoal); - goals.add(scoreGoal); + assert INSTANCE.getPlayer() != null; + assert INSTANCE.getStory() != null; + assert INSTANCE.getGoals() != null; + /* cleanup previous game */ gameAlreadyWon = false; - game = new Game(player, INSTANCE.getStory(), goals); + gameOver.set(false); + gameWon.set(false); + + /* start new game */ + game = new Game(new Player(INSTANCE.getPlayer()), INSTANCE.getStory(), INSTANCE.getGoals()); Passage openingPassage = game.begin(); updateReactiveProperties(openingPassage); } @@ -87,13 +89,14 @@ public class PlaytroughController extends Controller { updateGoals(); updateGameState(); updateInventory(); + updateGameTitle(); } /** * Computes the current state of the game. * Updates the reactive properties based on the current state of the game. */ - private void updateGameState() { + public void updateGameState() { if (gameAlreadyWon) return; if (game.isGameWon()) { @@ -139,6 +142,20 @@ public class PlaytroughController extends Controller { inventory.addAll(getPlayer().getInventory()); } + /** + * Updates the game title based on the game state. + */ + private void updateGameTitle() { + System.out.println("value: " + gameWon.getValue() + " " + gameOver.getValue() + " " + gameAlreadyWon); + if (gameOver.getValue()) { + gameTitle.setValue("You died and lost the game!"); + } else if (gameWon.getValue()) { + gameTitle.setValue("You won! (You can still play on)"); + } else { + gameTitle.setValue("Playing: " + INSTANCE.getStory().getTitle()); + } + } + /** * Returns the title of the current passage as an observable StringProperty. * @return the title of the current passage. @@ -180,21 +197,29 @@ public class PlaytroughController extends Controller { } /** - * Returns the current story. + * Helper method that returns the current story. * @return the current story. */ - public Story getStory() { + private Story getStory() { return game.getStory(); } /** - * Returns the current player. + * Helper method that returns the current player. * @return the current player. */ - public Player getPlayer() { + private Player getPlayer() { return game.getPlayer(); } + /** + * Returns the name of the player. + * @return the name of the player. + */ + public String getPlayerName() { + return game.getPlayer().getName(); + } + /** * Returns the health of the player as an observable StringProperty. * @return the health of the player. @@ -234,4 +259,21 @@ public class PlaytroughController extends Controller { public BooleanProperty getGameWon() { return gameWon; } + + /** + * Returns the title of the game as an observable StringProperty. + * The title of the game will be the title of the story while playing. If not playing, it will show the status of the game. + * @return the title of the game. + */ + public StringProperty getGameTitle() { + return gameTitle; + } + + /** + * Returns the image of the character as an ImageView. + * @return the image of the character. + */ + public ImageView getCharacterImageView() { + return INSTANCE.getCharacterImageView(); + } } diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/StageManager.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/StageManager.java index 96c73522d804d2ef7904b16c39b4c0ab49e41d06..48da5eb67041adb4dcd7ccbcd136fda18931c0b4 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/StageManager.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/controller/StageManager.java @@ -1,6 +1,6 @@ package edu.ntnu.idatt2001.group_30.paths.controller; -import edu.ntnu.idatt2001.group_30.paths.view.View; +import edu.ntnu.idatt2001.group_30.paths.view.views.View; import java.util.Stack; import javafx.stage.Stage; @@ -71,6 +71,22 @@ public class StageManager { } } + /** + * Sets the current view of the stage to the previous instance of the given view class. + * If the view is not in the viewStack, an IllegalArgumentException is thrown. + * @param viewClass The class of the view to go back to. + * @throws IllegalArgumentException if the view is not in the viewStack. + */ + public void goBackTo(Class<? extends View<?>> viewClass) throws IllegalArgumentException { + while (viewStack.size() > 1 && !viewStack.peek().getClass().equals(viewClass)) { + popAndUpdate(); + } + + if (viewStack.size() == 1 && !viewStack.peek().getClass().equals(viewClass)) { + throw new IllegalArgumentException("The view " + viewClass.getSimpleName() + " is not in the viewStack."); + } + } + /** * Pushes the given view onto the viewStack and updates the stage. * @param view The view to push onto the viewStack. diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/Player.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/Player.java index cba971265903bca6a0c58ace519f243edf413391..d07ff536e61f4e3c9d16c4254edf064c61b9fd44 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/Player.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/Player.java @@ -37,6 +37,19 @@ public class Player { this.inventory = new ArrayList<>(); } + /** + * This constructor creates a new Player from an existing Player object. + * This creates a deep copy of the Player object. + * @param player The Player object to be copied. + */ + public Player(Player player) { + this.name = player.name; + this.health = player.health; + this.score = player.score; + this.gold = player.gold; + this.inventory = new ArrayList<>(player.inventory); + } + /** * The Builder class is used to create a Player object. * @param build The Builder object that contains the information needed to create a Player object. diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/Story.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/Story.java index 52fb33ad2ce9c63932975546711b4d960892a933..6246e946b64ee55e7a60827b5526ee710a6d1a5f 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/Story.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/Story.java @@ -38,6 +38,10 @@ public class Story { addPassage(this.openingPassage); } + public boolean goalsAreAchievable() { + return true; + } + /** * Adds a passage to the passages map * The link object used as the key in the map is made from properties of the passage diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/goals/InventoryGoal.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/goals/InventoryGoal.java index c3f282a2d67e81c6b2cdc521aba26b81e1a814e3..1f3318b3e5845ece3357dde7f124cf7a2a413702 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/goals/InventoryGoal.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/model/goals/InventoryGoal.java @@ -18,9 +18,9 @@ public class InventoryGoal implements Goal<List<String>> { * The constructor defines the items a player must have. * @param mandatoryItems Expected items, given as a List{@code <String>}. */ - public InventoryGoal(List<String> mandatoryItems) { + public InventoryGoal(List<String> mandatoryItems) throws IllegalArgumentException { + Objects.requireNonNull(mandatoryItems); this.mandatoryItems = mandatoryItems; - //TODO: Add exception? } /** diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/App.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/App.java index a1af9e280679e9a3c7fa8525740fec0628669896..8f18add2be461aff2bd711a220fe267c2aa003a8 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/App.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/App.java @@ -1,6 +1,7 @@ package edu.ntnu.idatt2001.group_30.paths.view; import edu.ntnu.idatt2001.group_30.paths.controller.StageManager; +import edu.ntnu.idatt2001.group_30.paths.view.views.HomeView; import javafx.application.Application; import javafx.stage.Stage; import javafx.stage.StageStyle; diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/components/ImageCarousel.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/components/ImageCarousel.java index 8ed8615ca58677ffdd2cacb669e0ade925288aa8..7858c3660988b7b2c5fc96b46d8ce4d473f7e1fe 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/components/ImageCarousel.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/components/ImageCarousel.java @@ -9,8 +9,6 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; -//TODO: make this into an HBOX with arrows that actually work. -// Add option of looping or not and adjust how arrows are display accordingly public class ImageCarousel { private final LinkedList<Image> images = new LinkedList<>(); @@ -50,7 +48,6 @@ public class ImageCarousel { HBox carousel = new HBox(leftButton, currentImage, rightButton); carousel.setAlignment(Pos.CENTER); - return carousel; } @@ -67,4 +64,8 @@ public class ImageCarousel { public int size() { return size; } + + public ImageView getCurrentImage() { + return currentImage; + } } diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/components/common/DefaultFont.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/components/common/DefaultFont.java index 688d263e3437cb88f7d491e440b9f2d3dfdb4619..1e3e66040fde016a1beebb8c292531f91db14331 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/components/common/DefaultFont.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/components/common/DefaultFont.java @@ -20,7 +20,7 @@ public class DefaultFont { * @return A JavaFX font. */ public static Font big() { - return new Font(DEFAULT_FONT, 48); + return new Font(DEFAULT_FONT, 42); } /** @@ -28,7 +28,7 @@ public class DefaultFont { * @return A JavaFX font. */ public static Font medium() { - return new Font(DEFAULT_FONT, 24); + return new Font(DEFAULT_FONT, 26); } /** diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/CreatePlayerView.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/CreatePlayerView.java similarity index 74% rename from src/main/java/edu/ntnu/idatt2001/group_30/paths/view/CreatePlayerView.java rename to src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/CreatePlayerView.java index 70ee42acc603f74a4b218d311949ed26d0da470e..b7eb60f988d387c435faa4081d2f3c6ae4aa3d2d 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/CreatePlayerView.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/CreatePlayerView.java @@ -1,4 +1,4 @@ -package edu.ntnu.idatt2001.group_30.paths.view; +package edu.ntnu.idatt2001.group_30.paths.view.views; import static edu.ntnu.idatt2001.group_30.paths.PathsSingleton.INSTANCE; @@ -17,7 +17,7 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.TextField; -import javafx.scene.image.ImageView; +import javafx.scene.image.*; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; @@ -97,11 +97,10 @@ public class CreatePlayerView extends View<BorderPane> { legsURIs.add("/images/GreenLegs.png"); ImageCarousel headCarousel = new ImageCarousel(headURIs); - ImageCarousel headCarousel2 = new ImageCarousel(torsoURIs); - ImageCarousel headCarousel3 = new ImageCarousel(legsURIs); - VBox centerBox = new VBox(headCarousel.getCarousel(), headCarousel2.getCarousel(), headCarousel3.getCarousel()); + ImageCarousel torsoCarousel = new ImageCarousel(torsoURIs); + ImageCarousel legsCarousel = new ImageCarousel(legsURIs); + VBox centerBox = new VBox(headCarousel.getCarousel(), torsoCarousel.getCarousel(), legsCarousel.getCarousel()); centerBox.setAlignment(Pos.CENTER); - leftVBox.getStyleClass().add("left-vbox"); nameField = new TextField(); @@ -114,7 +113,12 @@ public class CreatePlayerView extends View<BorderPane> { continueButton = new Button("Continue"); returnButton = new Button("Return"); - HBox bottomBox = new HBox(nameField, continueButton, returnButton); + HBox viewButtons = new HBox(returnButton, continueButton); + returnButton.setAlignment(Pos.CENTER_LEFT); + continueButton.setAlignment(Pos.CENTER_RIGHT); + viewButtons.setSpacing(200); + + VBox bottomBox = new VBox(nameField, viewButtons); bottomBox.setSpacing(20); bottomBox.setAlignment(Pos.CENTER); bottomBox.setPadding(new Insets(0, 0, 0, 0)); @@ -137,6 +141,24 @@ public class CreatePlayerView extends View<BorderPane> { INSTANCE.getPlayer().getGold() ) ); + + Image headImage = headCarousel.getCurrentImage().getImage(); + Image torsoImage = torsoCarousel.getCurrentImage().getImage(); + Image legsImage = legsCarousel.getCurrentImage().getImage(); + + WritableImage characterImage = new WritableImage( + (int) headImage.getWidth(), + (int) headImage.getHeight() * 3 + ); + PixelWriter writer = characterImage.getPixelWriter(); + + copyImageOnto(headImage, writer, 0); + copyImageOnto(torsoImage, writer, (int) headImage.getHeight()); + copyImageOnto(legsImage, writer, (int) ((int) headImage.getHeight() + torsoImage.getHeight())); + + ImageView characterImageView = new ImageView(characterImage); + INSTANCE.setCharacterImageView(characterImageView); + createPlayerController.goTo(NewGameView.class).handle(event); } catch (Exception e) { AlertDialog.showWarning(e.getMessage()); @@ -144,4 +166,13 @@ public class CreatePlayerView extends View<BorderPane> { }); returnButton.setOnAction(e -> StageManager.getInstance().goBack()); } + + private void copyImageOnto(Image image, PixelWriter writer, int yOffset) { + PixelReader reader = image.getPixelReader(); + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + writer.setColor(x, y + yOffset, reader.getColor(x, y)); + } + } + } } diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/HelpView.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/HelpView.java similarity index 95% rename from src/main/java/edu/ntnu/idatt2001/group_30/paths/view/HelpView.java rename to src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/HelpView.java index fd483d8093cde5960ee005f035811355a8ac382a..902171288bd77b406a5976ca30cf613a7177eb69 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/HelpView.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/HelpView.java @@ -1,4 +1,4 @@ -package edu.ntnu.idatt2001.group_30.paths.view; +package edu.ntnu.idatt2001.group_30.paths.view.views; import edu.ntnu.idatt2001.group_30.paths.controller.HelpController; import edu.ntnu.idatt2001.group_30.paths.controller.StageManager; diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/HomeView.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/HomeView.java similarity index 91% rename from src/main/java/edu/ntnu/idatt2001/group_30/paths/view/HomeView.java rename to src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/HomeView.java index c59218277f5d2194ba8b26940811e9bda038b9f6..55cbd5a9d909f0d08484b334dce398ba93f8a61a 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/HomeView.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/HomeView.java @@ -1,4 +1,4 @@ -package edu.ntnu.idatt2001.group_30.paths.view; +package edu.ntnu.idatt2001.group_30.paths.view.views; import edu.ntnu.idatt2001.group_30.paths.controller.HomeController; import edu.ntnu.idatt2001.group_30.paths.view.components.common.DefaultButton; @@ -43,7 +43,7 @@ public class HomeView extends View<VBox> { private List<Node> getStartMenuButtons() { List<Node> buttons = new ArrayList<>(); if (controller.canContinueAGame()) buttons.add( - DefaultButton.big("Continue", controller.goTo(PlaythroughView.class)) + DefaultButton.big("Continue", controller.goBackTo(PlaythroughView.class)) ); buttons.add(DefaultButton.big("New game", controller.goTo(CreatePlayerView.class))); buttons.add(DefaultButton.big("Help", controller.goTo(HelpView.class))); diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/NewGameView.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/NewGameView.java similarity index 94% rename from src/main/java/edu/ntnu/idatt2001/group_30/paths/view/NewGameView.java rename to src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/NewGameView.java index 3372d1199acb6db1862527b97117a5a2e49637ad..e4a5469cff642c6e9e718b708df66e7649df3c17 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/NewGameView.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/NewGameView.java @@ -1,9 +1,10 @@ -package edu.ntnu.idatt2001.group_30.paths.view; +package edu.ntnu.idatt2001.group_30.paths.view.views; import static edu.ntnu.idatt2001.group_30.paths.PathsSingleton.INSTANCE; import edu.ntnu.idatt2001.group_30.paths.controller.NewGameController; import edu.ntnu.idatt2001.group_30.paths.controller.StageManager; +import edu.ntnu.idatt2001.group_30.paths.view.StoryDisplay; import edu.ntnu.idatt2001.group_30.paths.view.components.common.DefaultButton; import edu.ntnu.idatt2001.group_30.paths.view.components.pop_up.AlertDialog; import java.io.File; @@ -56,13 +57,8 @@ public class NewGameView extends View<BorderPane> { Button backButton = new Button("Back"); backButton.setOnAction(e -> StageManager.getInstance().goBack()); - /* - Button startButton = new Button("Start"); - startButton.setOnAction(e -> { - newGameController.goTo(PlaythroughView.class); - }); - */ - Button startButton = DefaultButton.medium("Start game", newGameController.goTo(PlaythroughView.class)); + startButton = DefaultButton.medium("Start game", newGameController.goTo(PlaythroughView.class)); + startButton.setVisible(false); HBox buttonBox = new HBox(10, backButton, startButton); buttonBox.setAlignment(Pos.CENTER); diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/PlaythroughView.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/PlaythroughView.java similarity index 73% rename from src/main/java/edu/ntnu/idatt2001/group_30/paths/view/PlaythroughView.java rename to src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/PlaythroughView.java index dac00a4cd11af21316e5f6288eb4dd1da1a60bef..bc3fbd256c184a15e97b557b6c93a1dfe50ffef6 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/PlaythroughView.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/PlaythroughView.java @@ -1,23 +1,28 @@ -package edu.ntnu.idatt2001.group_30.paths.view; +package edu.ntnu.idatt2001.group_30.paths.view.views; import edu.ntnu.idatt2001.group_30.paths.controller.PlaytroughController; -import edu.ntnu.idatt2001.group_30.paths.model.*; +import edu.ntnu.idatt2001.group_30.paths.model.Link; import edu.ntnu.idatt2001.group_30.paths.model.goals.Goal; import edu.ntnu.idatt2001.group_30.paths.view.components.common.DefaultButton; import edu.ntnu.idatt2001.group_30.paths.view.components.common.DefaultText; import edu.ntnu.idatt2001.group_30.paths.view.components.common.Ref; import edu.ntnu.idatt2001.group_30.paths.view.components.pop_up.AlertDialog; +import java.net.URL; import javafx.collections.ListChangeListener; import javafx.collections.MapChangeListener; import javafx.collections.ObservableList; import javafx.collections.ObservableMap; +import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.Node; +import javafx.scene.control.Button; import javafx.scene.control.ScrollPane; import javafx.scene.control.Separator; +import javafx.scene.image.ImageView; import javafx.scene.layout.*; import javafx.scene.paint.Color; +import javafx.scene.shape.Box; import javafx.scene.text.Text; /** @@ -102,7 +107,7 @@ public class PlaythroughView extends View<VBox> { header.setBackground(new Background(new BackgroundFill(Color.LIGHTBLUE, null, null))); /* header content */ - header.getChildren().add(DefaultText.big("Playing: " + controller.getStory().getTitle())); + header.getChildren().add(Ref.bigText(controller.getGameTitle())); header.getChildren().add(new Separator(Orientation.VERTICAL)); header.getChildren().add(DefaultButton.medium("Home", controller.goTo(HomeView.class))); header @@ -133,43 +138,57 @@ public class PlaythroughView extends View<VBox> { private Node playtroughBox(Pane parentPane) { /* container box configuration */ VBox playtrough = new VBox(); - playtrough.prefWidthProperty().bind(parentPane.widthProperty().multiply(0.7)); + playtrough.getStyleClass().add("box-shadow"); + playtrough.prefWidthProperty().bind(parentPane.widthProperty().multiply(0.6)); playtrough.prefHeightProperty().bind(parentPane.heightProperty()); playtrough.setAlignment(Pos.TOP_CENTER); playtrough.setBackground(new Background(new BackgroundFill(Color.LIGHTBLUE, new CornerRadii(30), null))); /* Passage title */ - playtrough.getChildren().add(Ref.bigText(controller.getPassageTitle())); + playtrough.getChildren().add(Ref.mediumText(controller.getPassageTitle())); /* Passage content */ ScrollPane passagePane = new ScrollPane(); passagePane.paddingProperty().setValue(new javafx.geometry.Insets(20)); passagePane.setMinHeight(350); - Text content = Ref.mediumText(controller.getPassageContent()); + Text content = Ref.smallText(controller.getPassageContent()); content.wrappingWidthProperty().bind(passagePane.widthProperty().multiply(0.85)); passagePane.setContent(content); playtrough.getChildren().add(passagePane); /* links */ + ScrollPane linksBoxWrapper = new ScrollPane(); + linksBoxWrapper.prefHeightProperty().bind(parentPane.heightProperty().multiply(0.5)); + linksBoxWrapper.centerShapeProperty().setValue(true); VBox linksBox = new VBox(); linksBox.setAlignment(Pos.TOP_CENTER); + linksBox.prefWidthProperty().bind(linksBoxWrapper.widthProperty()); + linksBox.setSpacing(10); ObservableList<Link> links = controller.getLinks(); links.addListener( (ListChangeListener<Link>) change -> { linksBox.getChildren().clear(); - for (Link link : links) { - linksBox.getChildren().add(DefaultButton.big(link.getText(), e -> controller.chooseLink(link))); - } + populateLinksBox(linksBox, links); } ); - for (Link link : links) { - linksBox.getChildren().add(DefaultButton.big(link.getText(), e -> controller.chooseLink(link))); - } - playtrough.getChildren().add(linksBox); + populateLinksBox(linksBox, links); + linksBoxWrapper.setContent(linksBox); + playtrough.getChildren().add(linksBoxWrapper); return playtrough; } + private void populateLinksBox(VBox linksBox, ObservableList<Link> links) { + linksBox.getChildren().clear(); + for (Link link : links) { + Button button = DefaultButton.medium(link.getText(), e -> controller.chooseLink(link)); + button.setWrapText(true); + button.setPrefWidth(300); + button.getStyleClass().add("link-button"); + linksBox.getChildren().add(button); + } + } + /** * Creates the info box. * It contains the current player information, like health, score and gold. @@ -179,11 +198,11 @@ public class PlaythroughView extends View<VBox> { */ private Node infoBox(Pane parentPane) { VBox infoBox = new VBox(); - infoBox.prefWidthProperty().bind(parentPane.widthProperty().multiply(0.3)); + infoBox.prefWidthProperty().bind(parentPane.widthProperty().multiply(0.4)); infoBox.prefHeightProperty().bind(parentPane.heightProperty()); infoBox.setAlignment(Pos.TOP_CENTER); - infoBox.setSpacing(10); - infoBox.setPadding(new javafx.geometry.Insets(10)); + infoBox.setSpacing(20); + infoBox.setPadding(new Insets(0, 0, 0, 20)); // Set 20 pixels of left padding /* player information */ infoBox.getChildren().add(playerInfo(infoBox)); @@ -201,34 +220,58 @@ public class PlaythroughView extends View<VBox> { * @return The parent node of the player information box. */ private Node playerInfo(Pane parentPane) { + /* + * Player info: + * player info | image + */ VBox playerInfoBox = new VBox(); + playerInfoBox.getStyleClass().add("box-shadow"); playerInfoBox.prefHeightProperty().bind(parentPane.heightProperty().multiply(0.3)); playerInfoBox.setBackground(new Background(new BackgroundFill(Color.LIGHTBLUE, new CornerRadii(30), null))); playerInfoBox.setAlignment(Pos.TOP_CENTER); playerInfoBox.setSpacing(10); - final Player player = controller.getPlayer(); + /* title */ + playerInfoBox.getChildren().add(DefaultText.medium("Player info:")); + + /* player data and image container */ + HBox playerDataAndImage = new HBox(); + playerDataAndImage.setAlignment(Pos.TOP_LEFT); - playerInfoBox.getChildren().add(DefaultText.medium("Game info:")); - playerInfoBox.getChildren().add(DefaultText.small("Player name: " + player.getName())); + /* player data */ + VBox playerData = new VBox(); + playerData.setAlignment(Pos.TOP_CENTER); + playerData.setSpacing(10); + playerData.prefWidthProperty().bind(playerInfoBox.widthProperty().multiply(0.5)); + playerData.getChildren().add(DefaultText.small("Name: " + controller.getPlayerName())); HBox score = new HBox(); score.setAlignment(Pos.TOP_CENTER); score.getChildren().add(DefaultText.small("Score: ")); score.getChildren().add(Ref.smallText(controller.getScore())); - playerInfoBox.getChildren().add(score); + playerData.getChildren().add(score); HBox health = new HBox(); health.setAlignment(Pos.TOP_CENTER); health.getChildren().add(DefaultText.small("Health: ")); health.getChildren().add(Ref.smallText(controller.getHealth())); - playerInfoBox.getChildren().add(health); + playerData.getChildren().add(health); HBox gold = new HBox(); gold.setAlignment(Pos.TOP_CENTER); gold.getChildren().add(DefaultText.small("Gold: ")); gold.getChildren().add(Ref.smallText(controller.getGold())); - playerInfoBox.getChildren().add(gold); + playerData.getChildren().add(gold); + + /* player image */ + ImageView characterImageView = controller.getCharacterImageView(); + characterImageView.setFitHeight(150); + characterImageView.setPreserveRatio(true); + + playerDataAndImage.getChildren().add(playerData); + playerDataAndImage.getChildren().add(characterImageView); + + playerInfoBox.getChildren().add(playerDataAndImage); return playerInfoBox; } @@ -240,10 +283,7 @@ public class PlaythroughView extends View<VBox> { * @return The parent node of the goal information box. */ private Node goalInfo(Pane parentPane) { - VBox goalInfoBox = new VBox(); - goalInfoBox.setAlignment(Pos.TOP_CENTER); - goalInfoBox.prefHeightProperty().bind(parentPane.heightProperty().multiply(0.3)); - goalInfoBox.setBackground(new Background(new BackgroundFill(Color.LIGHTBLUE, new CornerRadii(30), null))); + Pane goalInfoBox = initInfoCard(parentPane); /* title */ goalInfoBox.getChildren().add(DefaultText.medium("Goals:")); @@ -254,6 +294,7 @@ public class PlaythroughView extends View<VBox> { VBox content = new VBox(); content.setAlignment(Pos.TOP_LEFT); content.setSpacing(20); + content.setPadding(new Insets(0, 0, 0, 20)); ObservableMap<Goal, Boolean> goals = controller.getGoals(); goals.addListener( @@ -274,10 +315,7 @@ public class PlaythroughView extends View<VBox> { * @return The parent node of the inventory information box. */ private Node inventoryInfo(Pane parentPane) { - VBox inventoryInfoBox = new VBox(); - inventoryInfoBox.setAlignment(Pos.TOP_CENTER); - inventoryInfoBox.prefHeightProperty().bind(parentPane.heightProperty().multiply(0.3)); - inventoryInfoBox.setBackground(new Background(new BackgroundFill(Color.LIGHTBLUE, new CornerRadii(30), null))); + Pane inventoryInfoBox = initInfoCard(parentPane); /* title */ inventoryInfoBox.getChildren().add(DefaultText.medium("Inventory:")); @@ -302,6 +340,15 @@ public class PlaythroughView extends View<VBox> { return inventoryInfoBox; } + private Pane initInfoCard(Pane parentPane) { + VBox box = new VBox(); + box.getStyleClass().add("box-shadow"); + box.setAlignment(Pos.TOP_CENTER); + box.prefHeightProperty().bind(parentPane.heightProperty().multiply(0.3)); + box.setBackground(new Background(new BackgroundFill(Color.LIGHTBLUE, new CornerRadii(30), null))); + return box; + } + private void showInventory(ObservableList<String> inventory, Pane content) { content.getChildren().clear(); for (String item : inventory) { @@ -322,7 +369,17 @@ public class PlaythroughView extends View<VBox> { goalBox.setAlignment(Pos.TOP_LEFT); goalBox.setSpacing(10); goalBox.getChildren().add(DefaultText.small(goal.toString())); - goalBox.getChildren().add(DefaultText.small(completed ? "Completed" : "Not completed")); + //goalBox.getChildren().add(DefaultText.small(completed ? "Completed" : "Not completed")); + URL imageUrl = getClass().getResource("/images/checkbox-" + (completed ? "marked" : "blank") + ".png"); + if (imageUrl != null) { + ImageView imageView = new ImageView(imageUrl.toString()); + imageView.setFitWidth(20); + imageView.setPreserveRatio(true); + goalBox.getChildren().add(imageView); + } else { + System.err.println("Unable to load image: " + imageUrl); + } + content.getChildren().add(goalBox); }); } diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/View.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/View.java similarity index 94% rename from src/main/java/edu/ntnu/idatt2001/group_30/paths/view/View.java rename to src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/View.java index 24382883dbc1806f40387f3fc44ac938c305c1d7..0ba45db74582e52ac25b7b09f433cb7dc6d7e523 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/View.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/View.java @@ -1,4 +1,4 @@ -package edu.ntnu.idatt2001.group_30.paths.view; +package edu.ntnu.idatt2001.group_30.paths.view.views; import edu.ntnu.idatt2001.group_30.paths.view.components.common.DefaultText; import java.lang.reflect.InvocationTargetException; @@ -7,6 +7,7 @@ import java.util.Objects; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.layout.*; +import javafx.scene.shape.SVGPath; /** * A View is a wrapper for a JavaFX Pane. @@ -35,6 +36,7 @@ public class View<T extends Pane> { public View(Class<T> paneClass) { try { parentPane = paneClass.getDeclaredConstructor().newInstance(); + parentPane.getStylesheets().add(getClass().getResource("/stylesheet.css").toExternalForm()); } catch ( InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e ) { @@ -78,6 +80,7 @@ public class View<T extends Pane> { // The reason for using a BorderPane is that it is easy to add content to the top, bottom, left and right. // A view is free to override this method and use a different Pane. BorderPane wrapper = new BorderPane(); + wrapper.setCenter(parentPane); wrapper.setBottom(globalFooter()); wrapper.getStylesheets().add(stylesheet); diff --git a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/ViewFactory.java b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/ViewFactory.java similarity index 89% rename from src/main/java/edu/ntnu/idatt2001/group_30/paths/view/ViewFactory.java rename to src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/ViewFactory.java index 65e13d2005d73c5c7eded30943fbcea56e5a4d99..ce9cabadbe943f5063446ede9b28fa1894fc4ae0 100644 --- a/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/ViewFactory.java +++ b/src/main/java/edu/ntnu/idatt2001/group_30/paths/view/views/ViewFactory.java @@ -1,4 +1,4 @@ -package edu.ntnu.idatt2001.group_30.paths.view; +package edu.ntnu.idatt2001.group_30.paths.view.views; import java.lang.reflect.InvocationTargetException; diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 761e656e48dda034a2e09036f53379d3773988a9..e7626c24e21cbd777c172d66d1187bf7486cb596 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -11,4 +11,5 @@ module edu.ntnu.idatt2001.group_30.paths { exports edu.ntnu.idatt2001.group_30.paths.view.components.pane; exports edu.ntnu.idatt2001.group_30.paths.view.components.table; exports edu.ntnu.idatt2001.group_30.paths.view.components.pop_up; + exports edu.ntnu.idatt2001.group_30.paths.view.views; } diff --git a/src/main/resources/images/BlueTorso.png b/src/main/resources/images/BlueTorso.png index 1d12ff812edeb904934f8a2e7b92dec2b181b0cf..2c3c42e94c1fac5209282ffaaabd1a9892a60f19 100644 Binary files a/src/main/resources/images/BlueTorso.png and b/src/main/resources/images/BlueTorso.png differ diff --git a/src/main/resources/images/GreenTorso.png b/src/main/resources/images/GreenTorso.png index be5d2cf39a9b8db13c17f2f3453ab2566fb15043..5d767afa278d7543c6665d5e1e8ef5785fda4c6c 100644 Binary files a/src/main/resources/images/GreenTorso.png and b/src/main/resources/images/GreenTorso.png differ diff --git a/src/main/resources/images/RedTorso.png b/src/main/resources/images/RedTorso.png index 7f4639f2ea131d8328e9f3d2847f719bc65f1081..6641be0d86b0b162825314f21bd22db2d4b8ad37 100644 Binary files a/src/main/resources/images/RedTorso.png and b/src/main/resources/images/RedTorso.png differ diff --git a/src/main/resources/images/checkbox-blank.png b/src/main/resources/images/checkbox-blank.png new file mode 100644 index 0000000000000000000000000000000000000000..2f476be5e2850b492d5e05e50bcba763cc2b69ee Binary files /dev/null and b/src/main/resources/images/checkbox-blank.png differ diff --git a/src/main/resources/images/checkbox-marked.png b/src/main/resources/images/checkbox-marked.png new file mode 100644 index 0000000000000000000000000000000000000000..8f1ffe6337a91cd223336fe6c6dbc5da66206288 Binary files /dev/null and b/src/main/resources/images/checkbox-marked.png differ diff --git a/src/main/resources/story-files/Eilor.paths b/src/main/resources/story-files/Eilor.paths index b82cf8bd76d618ac5f6e451ddf7816b81d488f9d..dfe12dab345b8d7fee5abd9e8dd56922c056f1fa 100644 --- a/src/main/resources/story-files/Eilor.paths +++ b/src/main/resources/story-files/Eilor.paths @@ -4,6 +4,7 @@ A story of War an Eilor It was a dark and stormy night. You were walking through the woods when you came upon a large, old house. [Try to open the door](Enter the house) [Look inside the window](Window peek) +[Look sussy inside the window](Window peek) ::Enter the house The door is locked. You try to open it, but it won't budge. You pick up an apple. diff --git a/src/main/resources/stylesheet.css b/src/main/resources/stylesheet.css index 2ed282a31ddff4933345d4f23df565c7bd7120cb..2d16dd20a814676381d110b5100cdbcad85c47eb 100644 --- a/src/main/resources/stylesheet.css +++ b/src/main/resources/stylesheet.css @@ -69,6 +69,19 @@ -fx-padding: 10; } +Button { + -fx-background-color: #3f51b5; + -fx-text-fill: #ffffff; + -fx-border-color: #303f9f; + -fx-border-width: 1px; + -fx-padding: 10 20; + -fx-font-weight: bold; +} + +Button:hover { + -fx-background-color: #303f9f; +} + .stats-button, .goals-button { -fx-background-color: #64b5f6; @@ -86,7 +99,7 @@ .continue-button, .return-button { - -fx-background-color: #4caf50; + -fx-background-color: #3fb551; -fx-text-fill: #ffffff; -fx-border-color: #43a047; -fx-border-width: 1px; @@ -103,3 +116,11 @@ -fx-spacing: 20; -fx-padding: 20; } + +.box-shadow { + -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 10, 0, 0, 0); +} + +.link-button { + -fx-background-radius: 20; +}