Skip to content

Commit

Permalink
add: Heartsystem, warning-popups, weekendreport, styling and major bu…
Browse files Browse the repository at this point in the history
…g fixes
  • Loading branch information
peretr committed May 14, 2026
2 parents 172413c + 3c2fe1b commit e3cf57c
Show file tree
Hide file tree
Showing 24 changed files with 282 additions and 239 deletions.
24 changes: 24 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/Audio.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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.
*
* <p>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.</p>
*/
public final class AudioManager {

private static final AudioManager INSTANCE = new AudioManager();
public class AudioManager {

private final Map<SoundEffect, AudioClip> clips = new EnumMap<>(SoundEffect.class);
private double volume = 1.0;
private static final AudioManager instance = new AudioManager();
private final Map<Audio, String> 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;
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -72,7 +75,10 @@ public StockAppController(
}
});

stockApp.getConfirmButton().setOnAction(e -> handleTransaction());
stockApp.getConfirmButton().setOnAction(e -> {
audioManager.playSFX(Audio.TRANSACTION);
handleTransaction();
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -15,6 +17,9 @@ public final class GameController {
private final List<AppController> 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.
Expand All @@ -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;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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();

}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,104 +1,76 @@
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<Popup> popups;
private final AudioManager audioManager = AudioManager.getInstance();
private AudioManager audioManager = AudioManager.getInstance();

public PopupsController(final List<Popup> popups) {
public PopupsController(List<Popup> 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;
}

public List<Popup> 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();
}
});
Expand All @@ -110,4 +82,4 @@ private void makeDraggable(final Popup popup) {
event.getSceneY() - offset[1], parentBounds[1])));
});
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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};

Expand All @@ -38,6 +43,7 @@ public void nextTick() {
updateGameState();
updateWeather();
hour.set(0);
Platform.runLater(() -> audioManager.playSFX(Audio.CLOCK));
} else {
hour.set(nextHour);
}
Expand Down
Loading

0 comments on commit e3cf57c

Please sign in to comment.