Skip to content

135 save states with stock file integration #136

Merged
merged 7 commits into from
May 26, 2026
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,7 @@ failsafe-reports/
coverage/
jacoco.exec

# ====== Stock data files ======= #
.txt
# ====== Stock data files and save files ======= #
*.txt
*.json
*.csv
190 changes: 92 additions & 98 deletions src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
import edu.ntnu.idi.idatt2003.g40.mappe.service.SaveGameService;
import edu.ntnu.idi.idatt2003.g40.mappe.service.StockFileManager;
import edu.ntnu.idi.idatt2003.g40.mappe.service.StockFileParser;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventData;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventSubscriber;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventType;
import edu.ntnu.idi.idatt2003.g40.mappe.utils.ConfigValues;
import edu.ntnu.idi.idatt2003.g40.mappe.utils.ThemeManager;
import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewManager;
Expand All @@ -24,18 +27,17 @@
import edu.ntnu.idi.idatt2003.g40.mappe.view.playgame.PlayGameView;
import edu.ntnu.idi.idatt2003.g40.mappe.view.settings.SettingsController;
import edu.ntnu.idi.idatt2003.g40.mappe.view.settings.SettingsView;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;

import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.WidgetEnum;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.financialsummary.SummaryController;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.financialsummary.SummaryView;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.dashboard.DashBoardController;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.dashboard.DashBoardView;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.financialsummary.SummaryController;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.financialsummary.SummaryView;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.market.MarketController;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.market.MarketView;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.minigames.*;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.minigames.GameEngineController;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.minigames.GameEngineView;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.minigames.MiniGamesController;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.minigames.MiniGamesView;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.minigames.games.ClickerGame;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.minigames.games.FindStockGame;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.minigames.games.TimeInputsGame;
Expand All @@ -45,6 +47,9 @@
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.topbar.TopBarView;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions.TransactionsController;
import edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.transactions.TransactionsView;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
Expand All @@ -54,23 +59,31 @@
/**
* Main class.
*
* <p>
* Extends {@link Application}
* </p>
* <p>Extends {@link Application}</p>
*
* <p>
* Initializes the application through the javafx framework.
* </p>
*/
* <p>Launches the javafx thread and starts the program.</p>
* */
public class Main extends Application {

static void main(String[] args) {
/**
* Active {@link Exchange} object.
* */
private Exchange exchange;

/**
* Active {@link Player} object.
* */
private Player player;

/**
* Main method, launches the start javafx method.
*
* @param args standard Java parameter.
* */
public static void main(final String[] args) {
launch(args);
}

/**
* {@inheritDoc}
*/
@Override
public void start(final Stage stage) throws Exception {
Scene scene = new Scene(new Pane());
Expand All @@ -81,175 +94,156 @@ public void start(final Stage stage) throws Exception {
stage.setWidth(ConfigValues.VIEWPORT_WIDTH.getValue());
stage.setHeight(ConfigValues.VIEWPORT_HEIGHT.getValue());

// Register the scene with the theme manager so it can toggle dark/light.
ThemeManager.getInstance().registerScene(scene);

EventManager eventManager = new EventManager();
ViewManager viewManager = new ViewManager(stage, eventManager);

List<Stock> stocksInFile;
// Loads a default file in case of misreading (fallback)
StockFileManager fileManager = new StockFileManager("src/main/resources/sp500.csv");

StockFileParser fileParser = new StockFileParser();
stocksInFile = fileParser.getStocksFromStrings(fileManager.readFile());
List<Stock> stocksInFile = fileParser.getStocksFromStrings(fileManager.readFile());

Exchange exchange = new Exchange("Exchange", stocksInFile);
Player player = new Player("Player 1", new BigDecimal("10000"));
exchange = new Exchange("Exchange", stocksInFile);
player = new Player("Player 1", new BigDecimal("10000"));

// Bridges the save list to the live player + exchange. Subscribes
// to LOAD_SAVE; mutates state; publishes STATE_RESET for widgets.
GameStateLoader gameStateLoader = new GameStateLoader(
player, exchange, stocksInFile, eventManager);

// Main menu
MainMenuView mainMenuView = new MainMenuView();
new MainMenuController(mainMenuView, eventManager);

// Play game (mellom hovedmeny og spillet)
PlayGameView playGameView = new PlayGameView();
SaveGameService saveGameService = new SaveGameService();
PlayGameController playGameController =
new PlayGameController(playGameView, eventManager, saveGameService);
playGameController.refresh();


// Create game (mellom play-game og selve spillet)
CreateGameView createGameView = new CreateGameView();
CreateGameController createGameController =
new CreateGameController(createGameView, eventManager, saveGameService);
// Refresh save-listen etter at en ny save er skrevet til disk.
createGameController.setOnSaveCreated(playGameController::refresh);

// Settings
SettingsView settingsView = new SettingsView();
new SettingsController(settingsView, eventManager);

// Summary section of the top bar
SummaryView summaryView = new SummaryView();
new SummaryController(summaryView, eventManager, exchange, player);
SummaryController summaryController =
new SummaryController(summaryView, eventManager, exchange, player);

// Top bar with summary section
TopBarView topBarView = new TopBarView(summaryView);
TopBarController topBarController = new TopBarController(topBarView, eventManager);

// Top bar without summary section
TopBarView topBarView2 = new TopBarView();
new TopBarController(topBarView2, eventManager);

// Dashboard (default center-view in-game - første siden du ser)
DashBoardView dashBoardView = new DashBoardView();
new DashBoardController(dashBoardView,
eventManager,
player,
exchange,
stocksInFile);
DashBoardController dashBoardController =
new DashBoardController(dashBoardView, eventManager, player, exchange, stocksInFile);

// Stats page (Stats-knappen i topbaren tar deg hit)
StatsView statsView = new StatsView();
new StatsController(statsView, eventManager, player, exchange);
StatsController statsController =
new StatsController(statsView, eventManager, player, exchange);

// Market page (Market-knappen tar deg hit)
MarketView marketView = new MarketView();
new MarketController(marketView,
eventManager,
player,
exchange,
stocksInFile);

// In-game (Change "topBarView" to "topBarView2" if no summary section).
// Dashboard er default center-view.
MarketController marketController =
new MarketController(marketView, eventManager, player, exchange, stocksInFile);

InGameView inGameView = new InGameView(topBarView, dashBoardView.getRootPane());
InGameController inGameController = new InGameController(
inGameView,
eventManager
);
InGameController inGameController = new InGameController(inGameView, eventManager);

// Transaction history page
TransactionsView transactionsView = new TransactionsView();
TransactionsController transactionsController = new TransactionsController(
transactionsView,
eventManager,
player.getTransactionArchive());
transactionsView, eventManager, player.getTransactionArchive());

ClickerGame clickerGame = new ClickerGame();
FindStockGame findStockGame = new FindStockGame(
stocksInFile.stream()
.map(Stock::getSymbol)
.toList()
stocksInFile.stream().map(Stock::getSymbol).toList()
);
TimeInputsGame timeInputsGame = new TimeInputsGame();

GameEngineView gameEngineView = new GameEngineView();
GameEngineController gameEngineController = new GameEngineController(
gameEngineView,
eventManager,
stocksInFile.getFirst()
gameEngineView, eventManager, stocksInFile.getFirst()
);

MiniGamesView miniGamesView = new MiniGamesView();
new MiniGamesController(
miniGamesView,
eventManager,
stocksInFile.getFirst(),
gameEngineView,
gameEngineController,
clickerGame,
inGameView,
findStockGame,
timeInputsGame
MiniGamesController miniGamesController =
new MiniGamesController(
miniGamesView, eventManager, stocksInFile.getFirst(), gameEngineView,
gameEngineController, clickerGame, inGameView, findStockGame, timeInputsGame
);

// Wire top bar buttons til å bytte mellom dashboard / stats / market /
// transactions. Stats-knappen tar deg til stats-siden.
// Adds a generic event subscriber to the event manager
// that handles STATE_RESET events. These events are
// triggered when a new save file is loaded.
// We define this implementation here, because
// this class has the highest level overview of the application.
eventManager.addSubscriber(new EventSubscriber() {
@Override
public <T> void handleEvent(final EventData<T> eventData) {
if (eventData.data() instanceof SaveGame) {

if (createGameController.getExchange() != null && createGameController.getPlayer() != null) {
gameStateLoader.setExchange(createGameController.getExchange());

exchange = createGameController.getExchange();
player = createGameController.getPlayer();
} else {
exchange = gameStateLoader.getExchange();
player = gameStateLoader.getPlayer();
}

List<Stock> dynamicStocks = exchange.getStocks();

// Updates the various UI components that
// depend on an exchange, player, or list of stocks.
findStockGame.updateStockPool(dynamicStocks.stream().map(Stock::getSymbol).toList());
summaryController.handleContextUpdate(Main.this.exchange, Main.this.player);
dashBoardController.handleStockPoolUpdate(dynamicStocks);
statsController.handleContextUpdate(Main.this.exchange, Main.this.player);
marketController.handleStockPoolUpdate(Main.this.exchange, Main.this.player, dynamicStocks);

if (!dynamicStocks.isEmpty()) {
miniGamesController.setActiveStock(dynamicStocks.getFirst());
}
}
}
}, EventType.STATE_RESET);

topBarController.setMarketIntegration(
inGameView::changeCenterView,
dashBoardView.getRootPane(),
marketView.getRootPane(),
statsView.getRootPane(),
transactionsView.getRootPane(),
transactionsController::refresh,
inGameView::changeCenterView, dashBoardView.getRootPane(), marketView.getRootPane(),
statsView.getRootPane(), transactionsView.getRootPane(), transactionsController::refresh,
miniGamesView.getRootPane()
);

// In-game settings overlay (Dark / Light theme toggle).
InGameSettingsView inGameSettingsView = new InGameSettingsView();
InGameSettingsController inGameSettingsController =
new InGameSettingsController(inGameSettingsView, eventManager, inGameView);
topBarController.setSettingsAction(inGameSettingsController::show);

// Auto-save the currently active save when the player quits back
// to the main menu. Silent if no save is active (i.e. the user
// got into the in-game scene without going through the save list).
topBarController.setOnQuitToMainMenu(() -> {
System.out.println("[auto-save] Quit triggered, attempting snapshot...");
SaveGame snapshot = gameStateLoader.snapshotActiveSave();
if (snapshot == null) {
System.out.println("[auto-save] No active save - nothing to write.");
return;
}
System.out.println("[auto-save] Snapshot built for '" + snapshot.getName()
+ "', balance=" + snapshot.getBalance()
+ ", week=" + snapshot.getWeek()
+ ", shares=" + snapshot.getOwnedShares().size()
+ ", txns=" + snapshot.getTransactions().size());
try {
saveGameService.saveGame(snapshot);
System.out.println("[auto-save] Wrote save '" + snapshot.getName() + "' to disk.");
} catch (Exception e) {
System.err.println("[auto-save] Failed: " + e.getMessage());
e.printStackTrace();
}
});

// Register all views
viewManager.addView(mainMenuView);
viewManager.addView(playGameView);
viewManager.addView(createGameView);
viewManager.addView(settingsView);
viewManager.addView(inGameView);
viewManager.setScene(mainMenuView);

// Register all widgets
inGameController.addwidget(WidgetEnum.DASHBOARD, dashBoardView.getRootPane());
inGameController.addwidget(WidgetEnum.MARKET, marketView.getRootPane());
inGameController.addwidget(WidgetEnum.MINIGAMES_OVERVIEW, miniGamesView.getRootPane());
Expand All @@ -259,4 +253,4 @@ public void start(final Stage stage) throws Exception {

stage.show();
}
}
}
Loading