diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/Audio.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/Audio.java new file mode 100644 index 0000000..1008b05 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/Audio.java @@ -0,0 +1,24 @@ +package edu.ntnu.idi.idatt2003.gruppe42.Audio; + +public enum Audio { + + // Sound effects + OPEN_APP("/Audio/open_app.wav"), + CLOSE_APP("/Audio/close_app.wav"), + CLOCK("/Audio/clock.wav"), + TRANSACTION("/Audio/transaction.wav"), + + // Background music + WORK_THEME("/Audio/work_theme.mp3"), + WEEKEND_THEME("/Audio/weekend_theme.mp3"); + + private String path; + + Audio(String path) { + this.path = path; + } + + public String getPath() { + return path; + } +} diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/AudioManager.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/AudioManager.java index aa43a53..1d1bca0 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/AudioManager.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/AudioManager.java @@ -1,48 +1,60 @@ package edu.ntnu.idi.idatt2003.gruppe42.Audio; +import javafx.scene.media.Media; +import javafx.scene.media.MediaPlayer; + import java.util.EnumMap; import java.util.Map; -import javafx.scene.Node; -import javafx.scene.input.MouseEvent; -import javafx.scene.media.AudioClip; - -/** - * Zero-latency sound effect player. - * - *

All assets in {@link SoundEffect} are decoded into in-memory - * {@link AudioClip}s on first instantiation, so {@link #play(SoundEffect)} - * fires immediately without any I/O.

- */ -public final class AudioManager { - private static final AudioManager INSTANCE = new AudioManager(); +public class AudioManager { - private final Map clips = new EnumMap<>(SoundEffect.class); - private double volume = 1.0; + private static final AudioManager instance = new AudioManager(); + private final Map urlCache = new EnumMap<>(Audio.class); + private MediaPlayer bgPlayer; private AudioManager() { - for (SoundEffect effect : SoundEffect.values()) { - AudioClip clip = new AudioClip( - getClass().getResource(effect.getPath()).toExternalForm() - ); - clips.put(effect, clip); + for (Audio audio : Audio.values()) { + var url = getClass().getResource(audio.getPath()); + urlCache.put(audio, url.toExternalForm()); } } - public static AudioManager getInstance() { - return INSTANCE; + public static AudioManager getInstance() { return instance; } + + public void playSFX(Audio audio) { + String url = urlCache.get(audio); + + if (url == null) { + return; + } + + MediaPlayer sfxPlayer = new MediaPlayer(new Media(url)); + sfxPlayer.setOnEndOfMedia(() -> { + sfxPlayer.stop(); + sfxPlayer.dispose(); + }); + + sfxPlayer.play(); } - /** Plays the given sound at the current master volume. */ - public void play(final SoundEffect sound) { - AudioClip clip = clips.get(sound); - if (clip != null) { - clip.play(volume); + public void playBgMusic(Audio audio) { + stopBgMusic(); + + String url = urlCache.get(audio); + if (url == null) { + return; } + + bgPlayer = new MediaPlayer(new Media(url)); + bgPlayer.setVolume(1); + bgPlayer.play(); } - /** Sets the master volume in the range [0.0, 1.0]. */ - public void setVolume(final double volume) { - this.volume = Math.max(0.0, Math.min(1.0, volume)); + public void stopBgMusic() { + if (bgPlayer != null) { + bgPlayer.stop(); + bgPlayer.dispose(); + bgPlayer = null; + } } -} +} \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/SoundEffect.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/SoundEffect.java deleted file mode 100644 index bc72d1a..0000000 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/SoundEffect.java +++ /dev/null @@ -1,23 +0,0 @@ -package edu.ntnu.idi.idatt2003.gruppe42.Audio; - -/** - * Catalog of every short sound effect played by the game. - * The {@link AudioManager} pre-loads each entry on startup so that - * {@link AudioManager#play(SoundEffect)} fires with no fetch/decode lag. - */ -public enum SoundEffect { - OPEN_APP("/Audio/open_app.wav"), - CLOSE_APP("/Audio/close_app.wav"), - CLICK("/Audio/mouse_click.wav"); - - private final String path; - - SoundEffect(final String path) { - this.path = path; - } - - /** Classpath resource path of the audio asset. */ - public String getPath() { - return path; - } -} diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/AppControllers/StockAppController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/AppControllers/StockAppController.java index 7143144..d0ab31e 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/AppControllers/StockAppController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/AppControllers/StockAppController.java @@ -1,5 +1,7 @@ package edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers; +import edu.ntnu.idi.idatt2003.gruppe42.Audio.AudioManager; +import edu.ntnu.idi.idatt2003.gruppe42.Audio.Audio; import edu.ntnu.idi.idatt2003.gruppe42.Controller.MarketController; import edu.ntnu.idi.idatt2003.gruppe42.Model.Player; import edu.ntnu.idi.idatt2003.gruppe42.Model.Stock; @@ -28,6 +30,7 @@ public final class StockAppController implements AppController { private final StockApp stockApp; private final Player player; private Stock currentStock; + private AudioManager audioManager = AudioManager.getInstance(); /** True while the game is on Saturday or Sunday. */ private boolean isWeekend = false; @@ -72,7 +75,10 @@ public StockAppController( } }); - stockApp.getConfirmButton().setOnAction(e -> handleTransaction()); + stockApp.getConfirmButton().setOnAction(e -> { + audioManager.playSFX(Audio.TRANSACTION); + handleTransaction(); + }); } /** diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/GameController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/GameController.java index 0fb5fb2..9e554aa 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/GameController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/GameController.java @@ -1,5 +1,7 @@ package edu.ntnu.idi.idatt2003.gruppe42.Controller; +import edu.ntnu.idi.idatt2003.gruppe42.Audio.Audio; +import edu.ntnu.idi.idatt2003.gruppe42.Audio.AudioManager; import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.AppController; import edu.ntnu.idi.idatt2003.gruppe42.Model.GameState; @@ -15,6 +17,9 @@ public final class GameController { private final List appControllers = new ArrayList<>(); private Timer timer; private GameState gameState; + private AudioManager audioManager = AudioManager.getInstance(); + private boolean workMusicPlaying = false; + private boolean weekendMusicPlaying = false; /** * Adds an application controller to the list of controllers to be updated. @@ -28,6 +33,17 @@ public void addAppController(final AppController appController) { public void setGameState(GameState gameState) { this.gameState = gameState; + if (gameState == GameState.WORKDAY && !workMusicPlaying) { + audioManager.playBgMusic(Audio.WORK_THEME); + workMusicPlaying = true; + weekendMusicPlaying = false; + } + + if ((gameState == GameState.RENTDAY || gameState == GameState.FREEDAY) && !weekendMusicPlaying) { + audioManager.playBgMusic(Audio.WEEKEND_THEME); + weekendMusicPlaying = true; + workMusicPlaying = false; + } } /** diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/MarketController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/MarketController.java index 478d449..41294bd 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/MarketController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/MarketController.java @@ -15,7 +15,6 @@ public final class MarketController { private Exchange exchange; private final int stockResolution; - private StockApp stockApp; /** * Constructs a new MarketController and starts the market. @@ -64,8 +63,10 @@ public void updateMarket() { * Starts the market by performing initial updates. */ public void startMarket() { - for (int i = 0; i < stockResolution; i++) { - updateMarket(); + for (Stock stock : exchange.getAllStocks()) { + stock.fakeHistory(stockResolution); + stock.updateStockGraph(); + } } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/PopupsController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/PopupsController.java index 08d35fb..b2a0df1 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/PopupsController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/PopupsController.java @@ -1,76 +1,54 @@ package edu.ntnu.idi.idatt2003.gruppe42.Controller; import edu.ntnu.idi.idatt2003.gruppe42.Audio.AudioManager; -import edu.ntnu.idi.idatt2003.gruppe42.Audio.SoundEffect; +import edu.ntnu.idi.idatt2003.gruppe42.Audio.Audio; import edu.ntnu.idi.idatt2003.gruppe42.Model.App; import edu.ntnu.idi.idatt2003.gruppe42.View.Popup; import java.util.List; import javafx.scene.control.Button; -import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; /** * Manages popup visibility, dragging, and bounds-clamping. - * */ public final class PopupsController { private final List popups; - private final AudioManager audioManager = AudioManager.getInstance(); + private AudioManager audioManager = AudioManager.getInstance(); - public PopupsController(final List popups) { + public PopupsController(List popups) { this.popups = popups; popups.forEach(this::initPopup); } - private void initPopup(final Popup popup) { + private void initPopup(Popup popup) { makeDraggable(popup); - Button closeButton = popup.getCloseButton(); - closeButton.setOnAction(event -> hide(popup)); - - closeButton.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> { - if (popup.getRoot().isVisible()) { - audioManager.play(SoundEffect.CLOSE_APP); - } - }); + popup.getCloseButton().setOnAction(event -> hide(popup)); } - /** - * Wires an button to open the popup of the given type. - */ - public void bindOpenButton(final Button button, final App type) { - Popup target = findPopup(type); - button.setOnAction(event -> show(type)); - button.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> { - if (target != null && !target.getRoot().isVisible()) { - audioManager.play(SoundEffect.OPEN_APP); - } - }); - } - - /** - * Brings the popup of the given type to the front and shows it. - */ - public boolean show(final App type) { + public boolean show(App type) { if (type == null) { return false; } - Popup popup = findPopup(type); - if (popup == null) { - return false; - } - popup.getRoot().setVisible(true); - popup.getRoot().autosize(); - popup.getRoot().toFront(); + popups.stream() + .filter(popup -> popup.getType().equals(type)) + .findFirst() + .ifPresent(popup -> { + popup.getRoot().setVisible(true); + popup.getRoot().autosize(); + popup.getRoot().toFront(); + }); + audioManager.playSFX(Audio.OPEN_APP); return true; } - public boolean hide(final Popup popup) { + public boolean hide(Popup popup) { if (popup == null) { return false; } popup.getRoot().setVisible(false); + audioManager.playSFX(Audio.CLOSE_APP); return true; } @@ -78,27 +56,21 @@ public List getPopups() { return popups; } - private Popup findPopup(final App type) { - if (type == null) { - return null; - } - return popups.stream() - .filter(p -> p.getType().equals(type)) - .findFirst() - .orElse(null); + public void bindOpenButton(Button button, App type){ + button.setOnAction(event -> show(type)); } - private void makeDraggable(final Popup popup) { + private void makeDraggable(Popup popup) { BorderPane root = popup.getRoot(); double[] offset = new double[2]; - double[] parentBounds = new double[2]; + double[] parentBounds = new double[2]; // cache here popup.getHeader().setOnMousePressed(event -> { offset[0] = event.getSceneX() - root.getLayoutX(); offset[1] = event.getSceneY() - root.getLayoutY(); - + // snapshot bounds once on press, not on every drag tick if (root.getParent() instanceof Pane parent) { - parentBounds[0] = parent.getWidth() - popup.getWidth(); + parentBounds[0] = parent.getWidth() - popup.getWidth(); parentBounds[1] = parent.getHeight() - popup.getHeight(); } }); @@ -110,4 +82,4 @@ private void makeDraggable(final Popup popup) { event.getSceneY() - offset[1], parentBounds[1]))); }); } -} +} \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/TimeAndWeatherController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/TimeAndWeatherController.java index 67f9e25..89554bb 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/TimeAndWeatherController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/TimeAndWeatherController.java @@ -1,9 +1,13 @@ package edu.ntnu.idi.idatt2003.gruppe42.Controller; +import edu.ntnu.idi.idatt2003.gruppe42.Audio.AudioManager; +import edu.ntnu.idi.idatt2003.gruppe42.Audio.Audio; import edu.ntnu.idi.idatt2003.gruppe42.Controller.AppControllers.AppController; import edu.ntnu.idi.idatt2003.gruppe42.Model.Day; import edu.ntnu.idi.idatt2003.gruppe42.Model.GameState; import java.util.Random; + +import javafx.application.Platform; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; @@ -21,6 +25,7 @@ public class TimeAndWeatherController implements AppController { private final StringProperty weather = new SimpleStringProperty("Sunny"); private final Random random = new Random(); private final GameController gameController; + private AudioManager audioManager = AudioManager.getInstance(); private static final Day[] DAYS = {Day.SUN, Day.MON, Day.TUE, Day.WED, Day.THU, Day.FRI, Day.SAT}; @@ -38,6 +43,7 @@ public void nextTick() { updateGameState(); updateWeather(); hour.set(0); + Platform.runLater(() -> audioManager.playSFX(Audio.CLOCK)); } else { hour.set(nextHour); } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java index 20dc4b9..a648674 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/DesktopViewController.java @@ -38,8 +38,8 @@ import javafx.scene.layout.StackPane; /** - * Controller for the desktop view. Manages app popup lifecycle, drag-and-drop, - * the weekend rapport modal, warning popups, and game-over state. + * Controller for the desktop view. Manages app popup lifecycle, drag and drop, + * the weekend rapport modal, warning popups, and gameover state. */ public final class DesktopViewController { @@ -153,7 +153,7 @@ private void showWeekendRapport() { BigDecimal cashAfter = player.getMoney(); weekendRapportApp.populate(transactions, netIncome, cashBefore, WEEKLY_RENT, cashAfter); - weekendRapportApp.getRoot().setVisible(true); + weekendRapportApp.show(); desktopView.enterLayer(ModalLayer.RAPPORT); } @@ -173,7 +173,7 @@ private void handleAdvanceWeek() { } private void showDebtWarning() { - warningApp.configureDouble( + warningApp.configure( "💔", "Starting Week in Debt", "Your balance is negative. Proceeding to next week will cost you a life.", @@ -194,7 +194,7 @@ private void showDebtWarning() { } private void showMarketClosedWarning() { - warningApp.configureSingle( + warningApp.configure( "🔒", "Market Closed", "Trading is suspended on weekends.\nThe market reopens on Monday.", @@ -205,7 +205,7 @@ private void showMarketClosedWarning() { } private void showWarning() { - warningApp.getRoot().setVisible(true); + warningApp.show(); desktopView.enterLayer(ModalLayer.WARNING); } @@ -215,7 +215,7 @@ private void dismissWarning() { } private void showGameOver() { - gameOverApp.getRoot().setVisible(true); + gameOverApp.show(); desktopView.enterLayer(ModalLayer.GAME_OVER); } @@ -283,13 +283,13 @@ public Button createAppButton(final App type) { popupsController.bindOpenButton(button, type); button.setOnDragDetected(event -> { - Dragboard db = button.startDragAndDrop(TransferMode.MOVE); - db.setDragView(button.snapshot(null, null)); - db.setDragViewOffsetX(event.getX()); - db.setDragViewOffsetY(event.getY()); - ClipboardContent cc = new ClipboardContent(); - cc.putString("app_button"); - db.setContent(cc); + Dragboard dragboard = button.startDragAndDrop(TransferMode.MOVE); + dragboard.setDragView(button.snapshot(null, null)); + dragboard.setDragViewOffsetX(event.getX()); + dragboard.setDragViewOffsetY(event.getY()); + ClipboardContent clipboardContent = new ClipboardContent(); + clipboardContent.putString("app_button"); + dragboard.setContent(clipboardContent); event.consume(); }); diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/StartViewController.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/StartViewController.java index 849c16c..770211e 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/StartViewController.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Controller/ViewControllers/StartViewController.java @@ -40,7 +40,7 @@ public StartViewController(final Millions application, final StartView startView warningApp.centerInParent(); startView.getRoot().getChildren().add(warningApp.getRoot()); - warningApp.configureSingle( + warningApp.configure( "⚠", "Difficulty Required", "Choose how much starting money you want before jumping in.", @@ -56,7 +56,7 @@ public StartViewController(final Millions application, final StartView startView startView.getLoginButton().setOnAction(event -> { if (startView.getSelectedMode() == null) { - warningApp.getRoot().setVisible(true); + warningApp.show(); warningApp.getRoot().toFront(); return; } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Stock.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Stock.java index 7c847ce..a8f4f09 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Stock.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Model/Stock.java @@ -16,7 +16,7 @@ public class Stock { private final String symbol; private final String company; private StockGraph stockGraph; - private final List prices = new ArrayList<>(); + private List prices = new ArrayList<>(); /** * Constructs a new stock with symbol, company name, and initial sales price. @@ -124,6 +124,13 @@ public StockGraph getStockGraph() { return stockGraph; } + public void fakeHistory(int stockResolution) { + for (int i = 0; i < stockResolution; i++) { + updatePrice(); + } + prices = prices.reversed(); + } + /** * Updates the stock graph if it is visible. */ diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/GameOverApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/GameOverApp.java index 5b98342..033a7f9 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/GameOverApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/GameOverApp.java @@ -51,4 +51,9 @@ private void buildLayout() { public Button getStartOverButton() { return startOverButton; } + + public void show(){ + getRoot().setVisible(true); + getRoot().toFront(); + } } diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WarningApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WarningApp.java index fe3a249..422e298 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WarningApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WarningApp.java @@ -12,8 +12,6 @@ /** * Compact, reusable warning popup used across the entire game. * - *

Call {@link #configureSingle} for a single dismiss button, or - * {@link #configureDouble} for a two-button choice. Wire button actions after * configuring, then show the root and overlay via the desktop controller.

* *

The close button is always re-wired by the configure methods so it @@ -69,7 +67,7 @@ public WarningApp() { * @param message body text below the heading * @param primaryLabel label on the single action button */ - public void configureSingle( + public void configure( final String icon, final String title, final String message, @@ -99,7 +97,7 @@ public void configureSingle( * @param secondaryLabel label on the left / cancel button * @param primaryLabel label on the right / action button */ - public void configureDouble( + public void configure( final String icon, final String title, final String message, @@ -120,6 +118,11 @@ public void configureDouble( getCloseButton().setOnAction(event -> secondaryButton.fire()); } + public void show(){ + getRoot().setVisible(true); + getRoot().toFront(); + } + /** The right-side action button (always visible). Wire with {@code setOnAction}. */ public Button getPrimaryButton() { return primaryButton; diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WeekendRapportApp.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WeekendRapportApp.java index d304766..89dc2f5 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WeekendRapportApp.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Apps/WeekendRapportApp.java @@ -21,10 +21,10 @@ * *

Designed to read like a real weekly statement:

*
    - *
  1. Hero: the week's net income, large and color-coded
  2. + *
  3. Player summary: the week's net income, large and color-coded
  4. *
  5. Verdict: a one-line mood label (e.g. "Strong week")
  6. *
  7. Receipts: each transaction rendered with the shared {@link Receipt}
  8. - *
  9. Footer: cash → rent → balance, with the final balance large and colored
  10. + *
  11. Footer: cash, rent, and balance — with the final balance large and colored
  12. *
* *

Dismissed only via the "Continue to Sunday" button or by clicking the @@ -32,11 +32,10 @@ */ public class WeekendRapportApp extends Popup { - /** Threshold for "strong week" (net income covers rent.) */ private static final BigDecimal STRONG_WEEK_THRESHOLD = new BigDecimal("50.00"); - private final Label heroAmountLabel = new Label(); - private final Label heroCaptionLabel = new Label("Net Income This Week"); + private final Label playerAmountLabel = new Label(); + private final Label playerCaptionLabel = new Label("Net Income This Week"); private final Label verdictLabel = new Label(); private final Label cashLabel = new Label(); @@ -50,8 +49,7 @@ public class WeekendRapportApp extends Popup { public WeekendRapportApp() { super(740, 620, 0, 0, App.WEEKENDRAPPORT); - // Close button mirrors the continue action - getCloseButton().setOnAction(e -> continueButton.fire()); + getCloseButton().setOnAction(event -> continueButton.fire()); continueButton.getStyleClass().add("primary-button"); continueButton.setMaxWidth(Double.MAX_VALUE); @@ -60,112 +58,116 @@ public WeekendRapportApp() { content.setPadding(new Insets(28, 28, 24, 28)); content.setSpacing(18); + buildLayout(); } private void buildLayout() { - heroAmountLabel.getStyleClass().add("rapport-hero-amount"); - heroCaptionLabel.getStyleClass().add("rapport-hero-caption"); + playerAmountLabel.getStyleClass().add("rapport-player-amount"); + playerCaptionLabel.getStyleClass().add("rapport-player-caption"); verdictLabel.getStyleClass().add("rapport-verdict"); - VBox hero = new VBox(2, heroAmountLabel, heroCaptionLabel); - hero.setAlignment(Pos.CENTER); + VBox playerAmountBlock = new VBox(2, playerAmountLabel, playerCaptionLabel); + playerAmountBlock.setAlignment(Pos.CENTER); - VBox heroBlock = new VBox(10, hero, verdictLabel); - heroBlock.setAlignment(Pos.CENTER); - heroBlock.getStyleClass().add("rapport-hero-block"); - heroBlock.setPadding(new Insets(20, 16, 20, 16)); + VBox playerBlock = new VBox(10, playerAmountBlock, verdictLabel); + playerBlock.setAlignment(Pos.CENTER); + playerBlock.getStyleClass().add("rapport-player-block"); + playerBlock.setPadding(new Insets(20, 16, 20, 16)); - Label txHeader = sectionHeader("Transactions"); + Label transactionHeader = sectionHeader("Transactions"); - ScrollPane scroll = new ScrollPane(receiptList); - scroll.setFitToWidth(true); - scroll.getStyleClass().add("rapport-scroll"); - VBox.setVgrow(scroll, Priority.ALWAYS); + ScrollPane scrollPane = new ScrollPane(receiptList); + scrollPane.setFitToWidth(true); + scrollPane.getStyleClass().add("rapport-scroll"); + VBox.setVgrow(scrollPane, Priority.ALWAYS); cashLabel.getStyleClass().add("rapport-line-value"); rentLabel.getStyleClass().addAll("rapport-line-value", "rapport-rent-value"); - HBox cashLine = footerRow("Cash Available", cashLabel); - HBox rentLine = footerRow("Rent Due", rentLabel); - rentLine.getStyleClass().add("rapport-rent-row"); + HBox cashRow = footerRow("Cash Available", cashLabel); + HBox rentRow = footerRow("Rent Due", rentLabel); + rentRow.getStyleClass().add("rapport-rent-row"); - Region rule = new Region(); - rule.getStyleClass().add("rapport-rule"); - rule.setMinHeight(1); - rule.setMaxWidth(Double.MAX_VALUE); + Region dividerRule = new Region(); + dividerRule.getStyleClass().add("rapport-rule"); + dividerRule.setMinHeight(1); + dividerRule.setMaxWidth(Double.MAX_VALUE); - Label balanceHeader = new Label("Balance After Rent"); - balanceHeader.getStyleClass().add("rapport-balance-header"); + Label balanceHeaderLabel = new Label("Balance After Rent"); + balanceHeaderLabel.getStyleClass().add("rapport-balance-header"); balanceLabel.getStyleClass().add("rapport-balance-value"); - HBox balanceLine = new HBox(); - Region balSpacer = new Region(); - HBox.setHgrow(balSpacer, Priority.ALWAYS); - balanceLine.getChildren().addAll(balanceHeader, balSpacer, balanceLabel); - balanceLine.setAlignment(Pos.CENTER_LEFT); - balanceLine.setPadding(new Insets(2, 0, 0, 0)); + Region balanceSpacer = new Region(); + HBox.setHgrow(balanceSpacer, Priority.ALWAYS); + + HBox balanceRow = new HBox(); + balanceRow.getChildren().addAll(balanceHeaderLabel, balanceSpacer, balanceLabel); + balanceRow.setAlignment(Pos.CENTER_LEFT); + balanceRow.setPadding(new Insets(2, 0, 0, 0)); - VBox footer = new VBox(8, cashLine, rentLine, rule, balanceLine); + VBox footer = new VBox(8, cashRow, rentRow, dividerRule, balanceRow); content.getChildren().setAll( - heroBlock, - txHeader, scroll, + playerBlock, + transactionHeader, + scrollPane, footer, continueButton ); } - private static Label sectionHeader(final String text) { - Label l = new Label(text); - l.getStyleClass().add("rapport-section-header"); - return l; + private static Label sectionHeader(String text) { + Label label = new Label(text); + label.getStyleClass().add("rapport-section-header"); + return label; } - private static HBox footerRow(final String label, final Label valueLabel) { - Label key = new Label(label); - key.getStyleClass().add("rapport-line-key"); + private static HBox footerRow(String labelText, Label valueLabel) { + Label keyLabel = new Label(labelText); + keyLabel.getStyleClass().add("rapport-line-key"); + Region spacer = new Region(); HBox.setHgrow(spacer, Priority.ALWAYS); - HBox row = new HBox(key, spacer, valueLabel); + + HBox row = new HBox(keyLabel, spacer, valueLabel); row.setAlignment(Pos.CENTER_LEFT); return row; } - /** * Populates the rapport with the completed week's data. * Rent has already been deducted from the player's balance before this is called. * * @param transactions all transactions that occurred this week - * @param netIncome net cash change from trading (sales − purchases) + * @param netIncome net cash change from trading (sales minus purchases) * @param cashBeforeRent cash available before rent was deducted * @param rent the rent amount that was deducted * @param cashAfterRent cash balance after rent deduction (may be negative) */ public void populate( - final List transactions, - final BigDecimal netIncome, - final BigDecimal cashBeforeRent, - final BigDecimal rent, - final BigDecimal cashAfterRent + List transactions, + BigDecimal netIncome, + BigDecimal cashBeforeRent, + BigDecimal rent, + BigDecimal cashAfterRent ) { - heroAmountLabel.setText(formatSigned(netIncome)); - applyMoodClass(heroAmountLabel, moodFor(netIncome)); + playerAmountLabel.setText(formatSigned(netIncome)); + applyMoodStyle(playerAmountLabel, moodStyleFor(netIncome)); - String verdict = computeVerdict(netIncome, cashAfterRent); - verdictLabel.setText(verdict); - applyMoodClass(verdictLabel, moodFor(netIncome, cashAfterRent)); + verdictLabel.setText(computeVerdict(netIncome, cashAfterRent)); + applyMoodStyle(verdictLabel, moodStyleFor(netIncome, cashAfterRent)); receiptList.getChildren().clear(); + if (transactions.isEmpty()) { - Label empty = new Label("No transactions this week."); - empty.getStyleClass().add("rapport-line-key"); - empty.setPadding(new Insets(20)); - receiptList.getChildren().add(empty); + Label emptyMessage = new Label("No transactions this week."); + emptyMessage.getStyleClass().add("rapport-line-key"); + emptyMessage.setPadding(new Insets(20)); + receiptList.getChildren().add(emptyMessage); } else { - for (Transaction tx : transactions) { - receiptList.getChildren().add(Receipt.forTransaction(tx)); + for (Transaction transaction : transactions) { + receiptList.getChildren().add(Receipt.forTransaction(transaction)); } } @@ -183,69 +185,68 @@ public Button getContinueButton() { return continueButton; } - /** - * One-line mood label derived from net income and final balance. - * Reads as a quick gut-check for how the week went. - */ - private static String computeVerdict(final BigDecimal net, final BigDecimal balance) { + private static String computeVerdict(BigDecimal netIncome, BigDecimal balance) { if (balance.signum() < 0) { return "Week ended in debt"; } - if (net.compareTo(STRONG_WEEK_THRESHOLD) >= 0) { + if (netIncome.compareTo(STRONG_WEEK_THRESHOLD) >= 0) { return "Strong week"; } - if (net.signum() > 0) { + if (netIncome.signum() > 0) { return "Made some money"; } - if (net.signum() == 0) { + if (netIncome.signum() == 0) { return "Breaking even"; } return "Rough week"; } - /** Mood derived from net income alone (used for the hero amount). */ - private static String moodFor(final BigDecimal net) { - if (net.signum() > 0) { + private static String moodStyleFor(BigDecimal netIncome) { + if (netIncome.signum() > 0) { return "mood-positive"; } - if (net.signum() < 0) { + if (netIncome.signum() < 0) { return "mood-negative"; } return "mood-neutral"; } - /** Mood derived from net income AND final balance (used for the verdict). */ - private static String moodFor(final BigDecimal net, final BigDecimal balance) { + private static String moodStyleFor(BigDecimal netIncome, BigDecimal balance) { if (balance.signum() < 0) { return "mood-negative"; } - if (net.compareTo(STRONG_WEEK_THRESHOLD) >= 0) { + if (netIncome.compareTo(STRONG_WEEK_THRESHOLD) >= 0) { return "mood-positive"; } - if (net.signum() >= 0) { + if (netIncome.signum() >= 0) { return "mood-neutral"; } return "mood-negative"; } - private static void applyMoodClass(final Label label, final String moodClass) { + private static void applyMoodStyle(Label label, String moodStyle) { label.getStyleClass().removeAll("mood-positive", "mood-negative", "mood-neutral"); - label.getStyleClass().add(moodClass); + label.getStyleClass().add(moodStyle); } - private static String formatUsd(final BigDecimal amount) { + private static String formatUsd(BigDecimal amount) { return "$" + String.format("%,.2f", amount.abs()); } - private static String formatSigned(final BigDecimal amount) { + private static String formatSigned(BigDecimal amount) { String sign = amount.signum() >= 0 ? "+" : "−"; return sign + "$" + String.format("%,.2f", amount.abs()); } - static String formatBalance(final BigDecimal amount) { + static String formatBalance(BigDecimal amount) { if (amount.signum() < 0) { return "$" + String.format("%,.2f", amount.negate()) + " in debt"; } return "$" + String.format("%,.2f", amount); } -} + + public void show(){ + getRoot().setVisible(true); + getRoot().toFront(); + } +} \ No newline at end of file diff --git a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Views/DesktopView.java b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Views/DesktopView.java index 9263982..26a4d40 100644 --- a/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Views/DesktopView.java +++ b/src/main/java/edu/ntnu/idi/idatt2003/gruppe42/View/Views/DesktopView.java @@ -249,6 +249,7 @@ private HBox createBottomBar() { HBox heartsBox = buildHeartsBox(player); nextWeekButton = new Button("Advance to next week"); + nextWeekButton.getStyleClass().add("primary-button"); Region spacerLeft = new Region(); Region spacerRight = new Region(); @@ -324,7 +325,7 @@ private HBox createStatusBox() { twc.temperatureProperty().addListener((o, ov, nv) -> Platform.runLater(() -> temp.setText(nv + "°C"))); - HBox box = new HBox(10, weather, temp, day, week, clock); + HBox box = new HBox(10, weather, temp, clock, day, week); box.setAlignment(Pos.CENTER_RIGHT); box.setMinWidth(Region.USE_PREF_SIZE); return box; diff --git a/src/main/resources/Audio/clock.wav b/src/main/resources/Audio/clock.wav new file mode 100644 index 0000000..67e1002 Binary files /dev/null and b/src/main/resources/Audio/clock.wav differ diff --git a/src/main/resources/Audio/close_app.wav b/src/main/resources/Audio/close_app.wav index 55ea3aa..9b6291b 100644 Binary files a/src/main/resources/Audio/close_app.wav and b/src/main/resources/Audio/close_app.wav differ diff --git a/src/main/resources/Audio/mouse_click.wav b/src/main/resources/Audio/mouse_click.wav deleted file mode 100644 index 848eb7a..0000000 Binary files a/src/main/resources/Audio/mouse_click.wav and /dev/null differ diff --git a/src/main/resources/Audio/open_app.wav b/src/main/resources/Audio/open_app.wav index 1332cc1..5675422 100644 Binary files a/src/main/resources/Audio/open_app.wav and b/src/main/resources/Audio/open_app.wav differ diff --git a/src/main/resources/Audio/transaction.wav b/src/main/resources/Audio/transaction.wav new file mode 100644 index 0000000..6f135a9 Binary files /dev/null and b/src/main/resources/Audio/transaction.wav differ diff --git a/src/main/resources/Audio/weekend_theme.mp3 b/src/main/resources/Audio/weekend_theme.mp3 new file mode 100644 index 0000000..469e608 Binary files /dev/null and b/src/main/resources/Audio/weekend_theme.mp3 differ diff --git a/src/main/resources/Audio/work_theme.mp3 b/src/main/resources/Audio/work_theme.mp3 new file mode 100644 index 0000000..cc557cd Binary files /dev/null and b/src/main/resources/Audio/work_theme.mp3 differ diff --git a/src/main/resources/css/global.css b/src/main/resources/css/global.css index cc3ce8c..40680d1 100644 --- a/src/main/resources/css/global.css +++ b/src/main/resources/css/global.css @@ -2,26 +2,26 @@ .root { -fx-font-family: "Segoe UI", system-ui, -apple-system, sans-serif; - -primary: #007AFF; - -primary-hover: #0051d4; - - -success: #34C759; - -danger: #e54848; - -danger-soft: #ffe6e6; - -warning: #ff9500; - -accent: -danger; /* hearts, exit, destructive accents */ - - -text-strong: #1a1a1a; - -text-base: #333333; - -text-muted: #888888; - -text-faint: #aaaaaa; - - -surface: white; - -surface-soft: #f5f5f5; - -surface-line: #e8e8e8; - -popup-bg: white; - -popup-border: #d1d1d1; - -overlay: rgba(0, 0, 0, 0.45); + -primary: #007AFF; + -primary-hover: #0051d4; + + -success: #34C759; + -danger: #e54848; + -danger-soft: #ffe6e6; + -warning: #ff9500; + -accent: -danger; /* hearts, exit, destructive accents */ + + -text-strong: #1a1a1a; + -text-base: #333333; + -text-muted: #888888; + -text-faint: #aaaaaa; + + -surface: white; + -surface-soft: #f5f5f5; + -surface-line: #e8e8e8; + -popup-bg: white; + -popup-border: #d1d1d1; + -overlay: rgba(0, 0, 0, 0.45); } .scroll-pane > .viewport { diff --git a/src/main/resources/stocks.csv b/src/main/resources/stocks.csv index c9d57af..36eefea 100644 --- a/src/main/resources/stocks.csv +++ b/src/main/resources/stocks.csv @@ -16,4 +16,16 @@ UNI,NTNU Org.,311.14 COM,Communication Org.,155.28 MON,Monsters Inc.,329.54 AIR,Plane Inc.,240.70 -ATB,Bus Inc.,539.52 \ No newline at end of file +ATB,Bus Inc.,539.52 +BAT,Battery Inc.,243.43 +KWI,Kiwi Inc.,102,24 +VAC,Vacine Inc.,156.76 +JZZ,Jazz Inc.,230.40 +MET,Metal Inc.,302,89 +FRM,Farm Org.,260.76 +COS,Cosmetic Inc.,403.30 +TXI,Taxi Org.,239.70 +GOR,Gorilla Inc.,503.35 +MKY,Monkey Inc.,67.87 +NET,Network Org.,389.37 +WWW,Internet Org.,402.50 \ No newline at end of file