Skip to content

feat: game state saving and loading functionality#55

Merged
BakaAfk merged 4 commits intomasterfrom
dev
Nov 1, 2025
Merged

feat: game state saving and loading functionality#55
BakaAfk merged 4 commits intomasterfrom
dev

Conversation

@BakaAfk
Copy link
Collaborator

@BakaAfk BakaAfk commented Nov 1, 2025

  • fix: setting screen when open via pause screen is clickthrough-able

- Reduced default ball speed from 2000.0 to 1000.0.
- Added method to check if any sound is playing in SoundManager.
- Updated GameOverScreen to launch MainMenuScreen instead of going back to the previous screen.
- Adjustment to only update game in GameScreen when deltatime is not zero (when in pause state)
@BakaAfk BakaAfk requested review from Copilot and im-yuuki November 1, 2025 03:22
@BakaAfk BakaAfk self-assigned this Nov 1, 2025
Copy link
Owner

@im-yuuki im-yuuki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@BakaAfk BakaAfk merged commit 65c5fbb into master Nov 1, 2025
12 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a comprehensive game save/load system for the Exterminator3618 breakout game. It adds functionality to save game state, continue from saved games, and introduces a settings screen with music toggle capability.

  • Adds save/load functionality with JSON-based serialization of game state
  • Implements a "Continue" button on the main menu when a saved game exists
  • Adds music toggle feature in the settings screen
  • Refactors power-up type strings to be user-friendly display names

Reviewed Changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
level2.dat Updates level data format with row numbers and adjusted spacing
saved_game.json Adds example saved game state file with complete game data
SaveManager.java New utility class managing save/load operations via JSON serialization
GameSaveData.java New data transfer objects for game state serialization
SettingsScreen.java Adds music toggle button and refactors constructor field initialization
PauseScreen.java Adds save game functionality when returning to main menu and exposes game screen accessor
MainMenuScreen.java Implements continue game feature and repositions buttons
GameScreen.java Adds export/import state methods, wraps updates in deltaTime check, displays active power-ups
GameOverScreen.java Changes back button to explicitly launch main menu
SoundManager.java Reorders imports
PowerUp classes Updates power-up type names to human-readable strings
Ball.java Adds setter for combo count
Settings.java New class for global settings storage
Exterminator3618.java Calls show() when popping screen stack
Constants.java Reduces ball speed and adds save file path constant

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

private GameSaveData() { }

/**
* Game's infomation.
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'infomation' to 'information'.

Suggested change
* Game's infomation.
* Game's information.

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +33
final int CENTER_X = (Constants.WINDOW_WIDTH / 2) - (Constants.BUTTON_WIDTH / 2);
final int BACK_Y = (Constants.WINDOW_HEIGHT / 2) + Constants.BUTTON_HEIGHT - 20;
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These instance fields should be declared as private static final constants. Since they are calculated from other constants and never change, they should be static class-level constants, and they should have appropriate visibility modifiers.

Suggested change
final int CENTER_X = (Constants.WINDOW_WIDTH / 2) - (Constants.BUTTON_WIDTH / 2);
final int BACK_Y = (Constants.WINDOW_HEIGHT / 2) + Constants.BUTTON_HEIGHT - 20;
private static final int CENTER_X = (Constants.WINDOW_WIDTH / 2) - (Constants.BUTTON_WIDTH / 2);
private static final int BACK_Y = (Constants.WINDOW_HEIGHT / 2) + Constants.BUTTON_HEIGHT - 20;

Copilot uses AI. Check for mistakes.

viewport = new FitViewport(Constants.WINDOW_WIDTH, Constants.WINDOW_HEIGHT, camera);
touchPos = new Vector3();
backButton = new TextButton("Back", 100, 300, Constants.BUTTON_WIDTH, Constants.BUTTON_HEIGHT, true);
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the first constructor, the back button uses hardcoded coordinates (100, 300) instead of the CENTER_X and BACK_Y fields like the second constructor does. This creates inconsistent button positioning between the two constructors.

Suggested change
backButton = new TextButton("Back", 100, 300, Constants.BUTTON_WIDTH, Constants.BUTTON_HEIGHT, true);
backButton = new TextButton("Back", CENTER_X, BACK_Y, Constants.BUTTON_WIDTH, Constants.BUTTON_HEIGHT, true);

Copilot uses AI. Check for mistakes.
// Draw text here
box.draw(renderer);
// Update button text to reflect current setting
enableMusicButton.text = String.format("Music: %s", (Settings.enableMusic ? "On" : "Off"));
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directly mutating the text field of enableMusicButton breaks encapsulation. Consider adding a setText() method to the TextButton class instead of accessing the field directly.

Suggested change
enableMusicButton.text = String.format("Music: %s", (Settings.enableMusic ? "On" : "Off"));
enableMusicButton.setText(String.format("Music: %s", (Settings.enableMusic ? "On" : "Off")));

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
soundManager.stop();
GameScreen gs = new GameScreen(game);
SaveManager.loadInto(game, gs);
game.launchScreen(gs);
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The continueButton click handler doesn't check if a save exists before attempting to load. If the save file is deleted between rendering and clicking, this could cause unexpected behavior. Add SaveManager.hasSave(game) check before loading.

Suggested change
soundManager.stop();
GameScreen gs = new GameScreen(game);
SaveManager.loadInto(game, gs);
game.launchScreen(gs);
if (SaveManager.hasSave(game)) {
soundManager.stop();
GameScreen gs = new GameScreen(game);
SaveManager.loadInto(game, gs);
game.launchScreen(gs);
}

Copilot uses AI. Check for mistakes.
Comment on lines 102 to 104
screenStack.pop();
screenStack.peek().show();
if (screenStack.isEmpty()) {
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling screenStack.peek() before checking isEmpty() will throw EmptyStackException if popping removes the last screen. The order of these operations should be reversed: check isEmpty() first, then call peek().show() only if the stack is not empty.

Copilot uses AI. Check for mistakes.
if (handle.exists()) handle.delete();
}

public static void loadInto(Exterminator3618 game, GameScreen gameScreen) {
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter 'game' is never used.

Suggested change
public static void loadInto(Exterminator3618 game, GameScreen gameScreen) {
public static void loadInto(GameScreen gameScreen) {

Copilot uses AI. Check for mistakes.
return handle.exists() && handle.length() > 0;
}

public static void clearSave(Exterminator3618 game) {
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter 'game' is never used.

Suggested change
public static void clearSave(Exterminator3618 game) {
public static void clearSave() {

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +29
public static boolean hasSave(Exterminator3618 game) {
FileHandle handle = Gdx.files.local(SAVE_FILE);
return handle.exists() && handle.length() > 0;
}

public static void clearSave(Exterminator3618 game) {
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter 'game' is never used.

Suggested change
public static boolean hasSave(Exterminator3618 game) {
FileHandle handle = Gdx.files.local(SAVE_FILE);
return handle.exists() && handle.length() > 0;
}
public static void clearSave(Exterminator3618 game) {
public static boolean hasSave() {
FileHandle handle = Gdx.files.local(SAVE_FILE);
return handle.exists() && handle.length() > 0;
}
public static void clearSave() {

Copilot uses AI. Check for mistakes.

private SaveManager() { }

public static void saveGame(Exterminator3618 game, GameScreen gameScreen) {
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter 'game' is never used.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants