Skip to content

Commit

Permalink
Created a few popups to handle end of week, warnings and gameover
Browse files Browse the repository at this point in the history
  • Loading branch information
peretr committed May 13, 2026
1 parent 4b019e2 commit 172413c
Show file tree
Hide file tree
Showing 32 changed files with 1,701 additions and 508 deletions.
7 changes: 7 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Bash(del \"C:\\\\Users\\\\perer\\\\Documents\\\\GitHub\\\\Millions\\\\src\\\\main\\\\java\\\\edu\\\\ntnu\\\\idi\\\\idatt2003\\\\gruppe42\\\\View\\\\Apps\\\\DebtWarningApp.java\")"
]
}
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,48 @@
package edu.ntnu.idi.idatt2003.gruppe42.Audio;

import javafx.scene.media.AudioClip;

import java.util.EnumMap;
import java.util.Map;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.media.AudioClip;

public class AudioManager {
/**
* 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 AudioManager instance = new AudioManager();
private Map<SoundEffect, AudioClip> sounds = new EnumMap<>(SoundEffect.class);
private double volume = 1.0;
private static final AudioManager INSTANCE = new AudioManager();

public AudioManager() {
private final Map<SoundEffect, AudioClip> clips = new EnumMap<>(SoundEffect.class);
private double volume = 1.0;

private AudioManager() {
for (SoundEffect effect : SoundEffect.values()) {

AudioClip clip = new AudioClip(
getClass()
.getResource(effect.getPath())
.toExternalForm()
getClass().getResource(effect.getPath()).toExternalForm()
);

sounds.put(effect, clip);
clips.put(effect, clip);
}
}

public static AudioManager getInstance() {
return instance;
return INSTANCE;
}

public void play(SoundEffect sound) {
AudioClip audioClip = sounds.get(sound);

if (audioClip != null) {
audioClip.play(volume);
/** 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);
}
}

/** 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));
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
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");
CLOSE_APP("/Audio/close_app.wav"),
CLICK("/Audio/mouse_click.wav");

private String path;
private final String path;

SoundEffect(String path) {
SoundEffect(final String path) {
this.path = path;
}

/** Classpath resource path of the audio asset. */
public String getPath() {
return path;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
Expand All @@ -28,6 +29,12 @@ public final class StockAppController implements AppController {
private final Player player;
private Stock currentStock;

/** True while the game is on Saturday or Sunday. */
private boolean isWeekend = false;

/** Called when the player clicks Trade during a weekend. */
private Runnable onMarketClosed;

/**
* Constructs a new StockAppController.
*
Expand Down Expand Up @@ -158,10 +165,46 @@ protected void updateItem(final Stock stock, final boolean empty) {
};
}

/**
* Enables or disables trading based on whether it is a weekend.
* When weekend is {@code true} the Trade button is visually dimmed and
* the spinner is disabled; clicking Trade triggers the market-closed callback.
*
* @param weekend {@code true} on Saturday/Sunday, {@code false} on weekdays
*/
public void setWeekend(final boolean weekend) {
this.isWeekend = weekend;
Button tradeButton = stockApp.getConfirmButton();
if (weekend) {
tradeButton.getStyleClass().add("trade-button-weekend");
tradeButton.setOpacity(0.45);
stockApp.getQuantitySpinner().setDisable(true);
} else {
tradeButton.getStyleClass().remove("trade-button-weekend");
tradeButton.setOpacity(1.0);
stockApp.getQuantitySpinner().setDisable(false);
}
}

/**
* Registers the callback invoked when the player clicks Trade on a weekend.
*
* @param callback the action to run (typically shows a market-closed warning)
*/
public void setOnMarketClosed(final Runnable callback) {
this.onMarketClosed = callback;
}

/**
* Handles the trade button action based on the spinner value.
*/
private void handleTransaction() {
if (isWeekend) {
if (onMarketClosed != null) {
onMarketClosed.run();
}
return;
}
if (currentStock == null) {
return;
}
Expand Down Expand Up @@ -204,7 +247,7 @@ private void handleBuy(final Stock stock, final BigDecimal quantity) {
*/
private void handleSell(final Stock stock, final BigDecimal quantity) {
player.getPortfolio().getShares().stream()
.filter(s -> s.getStock().getSymbol().equals(stock.getSymbol()))
.filter(share -> share.getStock().getSymbol().equals(stock.getSymbol()))
.findFirst()
.ifPresent(existingShare -> {
if (existingShare.getQuantity().compareTo(quantity) < 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void setGameState(GameState gameState) {
public void startGame() {
timer = new Timer(true);
final int delay = 0;
final int period = 1000;
final int period = 50;

timer.scheduleAtFixedRate(
new TimerTask() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,67 +5,100 @@
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 AudioManager audioManager = AudioManager.getInstance();
private final AudioManager audioManager = AudioManager.getInstance();

public PopupsController(List<Popup> popups) {
public PopupsController(final List<Popup> popups) {
this.popups = popups;
popups.forEach(this::initPopup);
}

private void initPopup(Popup popup) {
private void initPopup(final Popup popup) {
makeDraggable(popup);
popup.getCloseButton().setOnAction(event -> hide(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);
}
});
}

public boolean show(App type) {
/**
* 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) {
if (type == null) {
return false;
}
popups.stream()
.filter(popup -> popup.getType().equals(type))
.findFirst()
.ifPresent(popup -> {
popup.getRoot().setVisible(true);
popup.getRoot().autosize();
popup.getRoot().toFront();
});
audioManager.play(SoundEffect.OPEN_APP);
Popup popup = findPopup(type);
if (popup == null) {
return false;
}
popup.getRoot().setVisible(true);
popup.getRoot().autosize();
popup.getRoot().toFront();
return true;
}

public boolean hide(Popup popup) {
public boolean hide(final Popup popup) {
if (popup == null) {
return false;
}
popup.getRoot().setVisible(false);
audioManager.play(SoundEffect.CLOSE_APP);
return true;
}

public List<Popup> getPopups() {
return popups;
}

private void makeDraggable(Popup popup) {
private Popup findPopup(final App type) {
if (type == null) {
return null;
}
return popups.stream()
.filter(p -> p.getType().equals(type))
.findFirst()
.orElse(null);
}

private void makeDraggable(final Popup popup) {
BorderPane root = popup.getRoot();
double[] offset = new double[2];
double[] parentBounds = new double[2]; // cache here
double[] parentBounds = new double[2];

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 @@ -77,4 +110,4 @@ private void makeDraggable(Popup popup) {
event.getSceneY() - offset[1], parentBounds[1])));
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ public TimeAndWeatherController(GameController gameController) {
public void nextTick() {
int nextHour = hour.get() + 1;
if (nextHour >= 24) {
System.out.println("This is the day" + dayIndex);
System.out.println("This is the hour" + hour);
dayIndex.set((dayIndex.get() + 1) % 7);
updateGameState();
updateWeather();
Expand All @@ -50,19 +48,16 @@ private void updateWeather() {
// Range from -10 to 30
int change = random.nextInt(11) - 5; // -5 to +5
int newTemp = temperature.get() + change;
if (newTemp < -10) newTemp = -10;
if (newTemp > 30) newTemp = 30;
newTemp = Math.min(Math.max(newTemp, -10), 30);
temperature.set(newTemp);

if (newTemp <= 0) {

if (random.nextBoolean()) {
weather.set("Sunny");
} else if (newTemp <= 0) {
weather.set("Snow");
} else {
// If warm, could be sunny or rain
if (random.nextBoolean()) {
weather.set("Sunny");
} else {
weather.set("Rain");
}
weather.set("Rain");
}
}

Expand Down Expand Up @@ -106,13 +101,10 @@ public String getWeekString() {
}

public void updateGameState() {

if (dayIndex.get() == 6) {
System.out.println("det er lørdag");
gameController.setGameState(GameState.RENTDAY);
return;
} else if (dayIndex.get() == 0) {
System.out.println("det er søndag");
gameController.setGameState(GameState.FREEDAY);
return;
}
Expand Down
Loading

0 comments on commit 172413c

Please sign in to comment.