Skip to content

Fix/play again bug #46

Merged
merged 2 commits into from
Apr 10, 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
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ public void listenToSessionState(String sessionId, SessionStateListener listener
}

@Override
public void updatePlayerScore(String sessionId, String playerId, int score, SimpleCallback callback) {
sessionRepository.updatePlayerScore(sessionId, playerId, score, callback);
public void updatePlayerScore(String sessionId, String playerId, int score, int roundScore, SimpleCallback callback) {
sessionRepository.updatePlayerScore(sessionId, playerId, score, roundScore, callback);
}

@Override
Expand All @@ -117,4 +117,9 @@ public void submitAnswer(String sessionId, int roundIndex, String playerId, Simp
public void listenToRoundAnswerCount(String sessionId, int roundIndex, AnswerCountCallback callback) {
sessionRepository.listenToRoundAnswerCount(sessionId, roundIndex, callback);
}

@Override
public void resetSessionForNewGame(String sessionId, SimpleCallback callback) {
sessionRepository.resetSessionForNewGame(sessionId, callback);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ public ListenerRegistration listenToPlayers(
player.setScore(scoreValue.intValue());
}

Long roundScoreValue = document.getLong("roundScore");
if (roundScoreValue != null) {
player.setLastRoundScore(roundScoreValue.intValue());
}

players.add(player);
});

Expand All @@ -184,6 +189,8 @@ public void fetchPlayers(
Player player = new Player(playerId, name, isHost);
Long scoreValue = document.getLong("score");
if (scoreValue != null) player.setScore(scoreValue.intValue());
Long roundScoreValue = document.getLong("roundScore");
if (roundScoreValue != null) player.setLastRoundScore(roundScoreValue.intValue());
players.add(player);
}
callback.onPlayersChanged(players);
Expand Down Expand Up @@ -412,10 +419,12 @@ public void updatePlayerScore(
String sessionId,
String playerId,
int score,
int roundScore,
FirebaseGateway.SimpleCallback callback
) {
Map<String, Object> update = new HashMap<>();
update.put("score", score);
update.put("roundScore", roundScore);

firestore.collection(SESSIONS)
.document(sessionId)
Expand All @@ -425,4 +434,51 @@ public void updatePlayerScore(
.addOnSuccessListener(unused -> callback.onSuccess())
.addOnFailureListener(callback::onFailure);
}

public void resetSessionForNewGame(
String sessionId,
FirebaseGateway.SimpleCallback callback
) {
// Delete all Answers first
firestore.collection(SESSIONS)
.document(sessionId)
.collection("Answers")
.get()
.addOnSuccessListener(answersSnap -> {
WriteBatch batch = firestore.batch();

for (DocumentSnapshot doc : answersSnap.getDocuments()) {
batch.delete(doc.getReference());
}

// Reset all player scores to 0 in the same batch
firestore.collection(SESSIONS)
.document(sessionId)
.collection(PLAYERS)
.get()
.addOnSuccessListener(playersSnap -> {
Map<String, Object> scoreReset = new HashMap<>();
scoreReset.put("score", 0);
scoreReset.put("roundScore", 0);
for (DocumentSnapshot p : playersSnap.getDocuments()) {
batch.update(p.getReference(), scoreReset);
}

// Reset session state + currentRound
Map<String, Object> sessionReset = new HashMap<>();
sessionReset.put("state", "lobby");
sessionReset.put("currentRound", 0);
batch.update(
firestore.collection(SESSIONS).document(sessionId),
sessionReset
);

batch.commit()
.addOnSuccessListener(u -> callback.onSuccess())
.addOnFailureListener(callback::onFailure);
})
.addOnFailureListener(callback::onFailure);
})
.addOnFailureListener(callback::onFailure);
}
}
3 changes: 3 additions & 0 deletions core/src/main/java/group07/beatbattle/BeatBattle.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public BeatBattle(FirebaseGateway firebaseGateway) {
private String localPlayerId;
private String localPlayerName;
private String localSessionId;
private String localGamePin;
private boolean localIsHost;

public void setServices(MusicService musicService, AudioPlayer audioPlayer) {
Expand All @@ -60,6 +61,8 @@ public void setLocalSession(String sessionId, boolean isHost) {
this.localIsHost = isHost;
}

public void setLocalGamePin(String gamePin) { this.localGamePin = gamePin; }
public String getLocalGamePin() { return localGamePin; }
public String getLocalSessionId() { return localSessionId; }
public boolean isLocalHost() { return localIsHost; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import group07.beatbattle.model.GameSession;
import group07.beatbattle.model.Leaderboard;
import group07.beatbattle.model.LeaderboardEntry;
import group07.beatbattle.model.GameMode;
import group07.beatbattle.states.GameOverState;
import group07.beatbattle.states.InRoundState;
import group07.beatbattle.states.LobbyState;
import group07.beatbattle.states.StartState;
import group07.beatbattle.states.StateManager;

Expand Down Expand Up @@ -93,6 +95,33 @@ public void onBackToMenu() {
StateManager.getInstance().setState(new StartState(game, new LobbyController(game)));
}

public void onPlayAgain() {
String sessionId = game.getLocalSessionId();
// Reset answers, scores, and state in Firebase, then go to lobby
game.getFirebaseGateway().resetSessionForNewGame(
sessionId,
new FirebaseGateway.SimpleCallback() {
@Override public void onSuccess() { goToLobby(); }
@Override public void onFailure(Exception e) { goToLobby(); }
}
);
}

private void goToLobby() {
Gdx.app.postRunnable(() ->
StateManager.getInstance().setState(new LobbyState(
game,
GameMode.CREATE,
game.getLocalSessionId(),
game.getLocalGamePin(),
game.getLocalPlayerId(),
game.getLocalPlayerName(),
true,
new LobbyController(game)
))
);
}

public void onGameOver() {
String sessionId = game.getLocalSessionId();
if (game.isLocalHost() && sessionId != null && !sessionId.isBlank()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public void onSuccess(SessionCreationResult result) {
Gdx.app.postRunnable(() -> {
game.setLocalPlayer(result.getHostId(), result.getHostName());
game.setLocalSession(result.getSessionId(), true);
game.setLocalGamePin(result.getGamePin());
view.setLoadingState(false);
view.setStatusMessage("");
StateManager.getInstance().setState(
Expand Down Expand Up @@ -114,6 +115,7 @@ public void onSuccess(SessionJoinResult result) {
Gdx.app.postRunnable(() -> {
game.setLocalPlayer(result.getPlayerId(), result.getPlayerName());
game.setLocalSession(result.getSessionId(), false);
game.setLocalGamePin(result.getGamePin());
view.setLoadingState(false);
view.setStatusMessage("");
StateManager.getInstance().setState(
Expand Down Expand Up @@ -272,6 +274,7 @@ private void startJoinerSyncListener(String sessionId, GameSession session) {
new FirebaseGateway.SessionStateListener() {
@Override
public void onStateChanged(String state, int currentRound) {
if ("STOPPED".equals(lastHandled[0])) return;
String key = state + ":" + currentRound;
if (key.equals(lastHandled[0])) return;
lastHandled[0] = key;
Expand All @@ -286,6 +289,21 @@ public void onStateChanged(String state, int currentRound) {
LeaderboardController lc = new LeaderboardController(game, session, lb);
StateManager.getInstance().setState(new GameOverState(game, lc));
});
} else if ("lobby".equals(state)) {
// Host pressed Play Again — kill this listener and return to lobby
lastHandled[0] = "STOPPED";
Gdx.app.postRunnable(() ->
StateManager.getInstance().setState(new LobbyState(
game,
GameMode.JOIN,
sessionId,
game.getLocalGamePin(),
game.getLocalPlayerId(),
game.getLocalPlayerName(),
false,
new LobbyController(game)
))
);
}
}

Expand All @@ -307,7 +325,10 @@ public void onPlayersChanged(List<Player> updated) {
Gdx.app.postRunnable(() -> {
for (Player u : updated) {
for (Player local : session.getPlayers()) {
if (local.getId().equals(u.getId())) local.setScore(u.getScore());
if (local.getId().equals(u.getId())) {
local.setScore(u.getScore());
local.setLastRoundScore(u.getLastRoundScore());
}
}
}
Leaderboard lb = new Leaderboard(session.getPlayers());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void onAnswerSubmitted(String answer) {
Player localPlayer = findLocalPlayer();
if (localPlayer != null) {
localPlayer.addScore(points);
submitScoreToFirebase(localPlayer.getId(), localPlayer.getScore());
submitScoreToFirebase(localPlayer.getId(), localPlayer.getScore(), localPlayer.getLastRoundScore());
}

playerAnswered = true;
Expand All @@ -79,7 +79,7 @@ public void onRoundExpired() {
if (!playerAnswered) {
Player localPlayer = findLocalPlayer();
if (localPlayer != null) {
submitScoreToFirebase(localPlayer.getId(), localPlayer.getScore());
submitScoreToFirebase(localPlayer.getId(), localPlayer.getScore(), localPlayer.getLastRoundScore());
}
recordAnswerInFirebase(); // count as answered so listener is consistent
}
Expand Down Expand Up @@ -156,14 +156,15 @@ private Player findLocalPlayer() {
return null;
}

private void submitScoreToFirebase(String playerId, int score) {
private void submitScoreToFirebase(String playerId, int score, int roundScore) {
String sessionId = game.getLocalSessionId();
if (sessionId == null || sessionId.isBlank()) return;

game.getFirebaseGateway().updatePlayerScore(
sessionId,
playerId,
score,
roundScore,
new FirebaseGateway.SimpleCallback() {
@Override public void onSuccess() {}
@Override public void onFailure(Exception exception) {
Expand Down Expand Up @@ -206,6 +207,7 @@ public void onPlayersChanged(List<Player> updatedPlayers) {
for (Player local : session.getPlayers()) {
if (local.getId().equals(updated.getId())) {
local.setScore(updated.getScore());
local.setLastRoundScore(updated.getLastRoundScore());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,17 @@ void deleteSession(

void listenToSessionState(String sessionId, SessionStateListener listener);

void updatePlayerScore(String sessionId, String playerId, int score, SimpleCallback callback);
void updatePlayerScore(String sessionId, String playerId, int score, int roundScore, SimpleCallback callback);

/** Records that a player has submitted an answer for the given round. */
void submitAnswer(String sessionId, int roundIndex, String playerId, SimpleCallback callback);

/** Fires with the current answer count whenever a new answer is submitted for a round. */
void listenToRoundAnswerCount(String sessionId, int roundIndex, AnswerCountCallback callback);

/** Clears Answers, resets all player scores to 0, and sets currentRound=0. Use before Play Again. */
void resetSessionForNewGame(String sessionId, SimpleCallback callback);

// --- Callbacks ---

interface GamePinExistsCallback {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public void listenToSessionState(String sessionId, SessionStateListener listener
}

@Override
public void updatePlayerScore(String sessionId, String playerId, int score, SimpleCallback callback) {
public void updatePlayerScore(String sessionId, String playerId, int score, int roundScore, SimpleCallback callback) {
callback.onFailure(new UnsupportedOperationException("Firebase is not available on this platform"));
}

Expand All @@ -112,4 +112,9 @@ public void submitAnswer(String sessionId, int roundIndex, String playerId, Simp
public void listenToRoundAnswerCount(String sessionId, int roundIndex, AnswerCountCallback callback) {
callback.onFailure(new UnsupportedOperationException("Firebase is not available on this platform"));
}

@Override
public void resetSessionForNewGame(String sessionId, SimpleCallback callback) {
callback.onFailure(new UnsupportedOperationException("Firebase is not available on this platform"));
}
}
1 change: 1 addition & 0 deletions core/src/main/java/group07/beatbattle/i18n/Strings.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public static String leavePlayerMsg() {
public static String gameOver() { return no ? "SPILLET ER OVER" : "GAME OVER"; }
public static String finalStandings(){ return no ? "Sluttresultater" : "Final Standings"; }
public static String backToMenu() { return no ? "Tilbake til menyen" : "Back to Menu"; }
public static String playAgain() { return no ? "Spill igjen" : "Play Again"; }

// --- Settings ---
public static String settings() { return no ? "Innstillinger" : "Settings"; }
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/group07/beatbattle/model/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ public void addScore(int points) {
public void setScore(int score) {
this.score = score;
}

public void setLastRoundScore(int roundScore) {
this.lastRoundScore = roundScore;
}
}
12 changes: 12 additions & 0 deletions core/src/main/java/group07/beatbattle/view/GameOverView.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ public GameOverView(BeatBattle game, LeaderboardController controller) {

root.add().expandY().row();

// Play Again button (host only)
if (controller.isHost()) {
BackButton playAgainButton = new BackButton(Strings.playAgain(), game.getMontserratFont());
playAgainButton.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
controller.onPlayAgain();
}
});
root.add(playAgainButton).width(600f).height(130f).padBottom(20f).row();
}

// Back to menu button
BackButton backButton = new BackButton(Strings.backToMenu(), game.getMontserratFont());
backButton.addListener(new ChangeListener() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ private Label createTitleLabel() {
private TextField createPlayerNameField() {
TextField textField = new TextField("", InputFieldStyles.createDefault(game.getMontserratFont()));
textField.setMessageText(Strings.enterName());
textField.setMaxLength(12);
return textField;
}

Expand Down
Loading
Loading