diff --git a/app/src/main/java/legion/muyue/best2048/MainActivity.java b/app/src/main/java/legion/muyue/best2048/MainActivity.java index fbba04b..cdd2ef6 100644 --- a/app/src/main/java/legion/muyue/best2048/MainActivity.java +++ b/app/src/main/java/legion/muyue/best2048/MainActivity.java @@ -9,6 +9,7 @@ package legion.muyue.best2048; import android.annotation.SuppressLint; import android.app.AlertDialog; +import android.content.DialogInterface; // Assurez-vous que cet import est présent import android.content.SharedPreferences; import android.os.Bundle; import android.util.TypedValue; @@ -28,7 +29,7 @@ import android.widget.Button; public class MainActivity extends AppCompatActivity { - + // --- UI Elements --- private GridLayout boardGridLayout; private TextView currentScoreTextView; private TextView highestScoreTextView; @@ -39,23 +40,23 @@ public class MainActivity extends AppCompatActivity { private ViewStub statisticsViewStub; private View inflatedStatsView; - + // --- Game Logic & Stats --- private Game game; private GameStats gameStats; private static final int BOARD_SIZE = 4; - + // --- State Management --- private boolean statisticsVisible = false; private enum GameFlowState { PLAYING, WON_DIALOG_SHOWN, GAME_OVER } private GameFlowState currentGameState = GameFlowState.PLAYING; - + // --- Preferences --- private SharedPreferences preferences; private static final String PREFS_NAME = "Best2048_Prefs"; private static final String HIGH_SCORE_KEY = "high_score"; private static final String GAME_STATE_KEY = "game_state"; - + // --- Activity Lifecycle --- @Override protected void onCreate(Bundle savedInstanceState) { @@ -71,17 +72,18 @@ public class MainActivity extends AppCompatActivity { @Override protected void onResume() { super.onResume(); - if (game != null && gameStats != null && !game.isGameOver() && !game.isGameWon()) { + // Redémarre le timer seulement si le jeu est en cours (pas gagné/perdu) + if (game != null && gameStats != null && currentGameState == GameFlowState.PLAYING) { gameStats.setCurrentGameStartTimeMs(System.currentTimeMillis()); } - + // Gère le réaffichage potentiel des stats si l'activité reprend if (statisticsVisible) { - if (inflatedStatsView != null) { - updateStatisticsTextViews(); + if (inflatedStatsView != null) { // Si déjà gonflé + updateStatisticsTextViews(); // Met à jour les données affichées inflatedStatsView.setVisibility(View.VISIBLE); multiplayerButton.setVisibility(View.GONE); } else { - + // Si pas encore gonflé (cas rare mais possible), on le fait afficher toggleStatistics(); } } @@ -90,18 +92,18 @@ public class MainActivity extends AppCompatActivity { @Override protected void onPause() { super.onPause(); - + // Sauvegarde l'état et les stats si le jeu existe if (game != null && gameStats != null) { - - if (!game.isGameOver() && !game.isGameWon()) { - gameStats.addPlayTime(System.currentTimeMillis() - gameStats.getCurrentGameStartTimeMs()); + // Met à jour le temps total SI la partie était en cours + if (currentGameState == GameFlowState.PLAYING) { + gameStats.addPlayTime(System.currentTimeMillis() - gameStats.getCurrentGameStartTimeMs()); // Utilise méthode GameStats } - saveGame(); - gameStats.saveStats(); + saveGame(); // Sauvegarde l'état du jeu (plateau + score courant) et le HS + gameStats.saveStats(); // Sauvegarde toutes les stats via GameStats } } - + // --- Initialisation --- /** * Récupère les références des vues du layout principal via leur ID. @@ -125,26 +127,18 @@ public class MainActivity extends AppCompatActivity { private void initializeGameAndStats() { preferences = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); gameStats = new GameStats(this); - loadGame(); + loadGame(); // Charge jeu et met à jour high score updateUI(); if (game == null) { + // Si loadGame échoue ou aucune sauvegarde, startNewGame gère l'initialisation startNewGame(); - } else { - - if (game.isGameOver()) { - currentGameState = GameFlowState.GAME_OVER; - } else if (game.isGameWon()) { - - currentGameState = GameFlowState.WON_DIALOG_SHOWN; - } else { - currentGameState = GameFlowState.PLAYING; - - } } + // L'état (currentGameState) est défini dans loadGame ou startNewGame } /** - * Attache les listeners aux boutons et configure le listener de swipe sur le plateau. + * Configure les listeners pour les boutons et le plateau de jeu (swipes). + * Mise à jour pour le bouton Menu. */ private void setupListeners() { newGameButton.setOnClickListener(v -> { @@ -155,18 +149,19 @@ public class MainActivity extends AppCompatActivity { v.startAnimation(AnimationUtils.loadAnimation(this, R.anim.button_press)); toggleStatistics(); }); + // Modifié pour appeler la nouvelle méthode showMenu() menuButton.setOnClickListener(v -> { v.startAnimation(AnimationUtils.loadAnimation(this, R.anim.button_press)); - showMenu(); + showMenu(); // Appelle la méthode du menu }); multiplayerButton.setOnClickListener(v -> { v.startAnimation(AnimationUtils.loadAnimation(this, R.anim.button_press)); - showMultiplayerScreen(); + showMultiplayerScreen(); // Affiche dialogue placeholder }); setupSwipeListener(); } - + // --- Mise à jour UI --- /** * Met à jour complètement l'interface utilisateur (plateau et scores). @@ -187,11 +182,11 @@ public class MainActivity extends AppCompatActivity { TextView tileTextView = new TextView(this); int value = game.getCellValue(row, col); setTileStyle(tileTextView, value); - + // Définit les LayoutParams pour que la tuile remplisse la cellule du GridLayout GridLayout.LayoutParams params = new GridLayout.LayoutParams(); - params.width = 0; params.height = 0; - params.rowSpec = GridLayout.spec(row, 1f); - params.columnSpec = GridLayout.spec(col, 1f); + params.width = 0; params.height = 0; // Poids gère la taille + params.rowSpec = GridLayout.spec(row, 1f); // Prend 1 fraction de l'espace en hauteur + params.columnSpec = GridLayout.spec(col, 1f); // Prend 1 fraction de l'espace en largeur int margin = (int) getResources().getDimension(R.dimen.tile_margin); params.setMargins(margin, margin, margin, margin); tileTextView.setLayoutParams(params); @@ -214,7 +209,6 @@ public class MainActivity extends AppCompatActivity { * @param value La valeur numérique de la tuile (0 pour vide). */ private void setTileStyle(TextView tileTextView, int value) { - tileTextView.setText(value > 0 ? String.valueOf(value) : ""); tileTextView.setGravity(Gravity.CENTER); tileTextView.setTypeface(null, android.graphics.Typeface.BOLD); @@ -241,7 +235,7 @@ public class MainActivity extends AppCompatActivity { } - + // --- Gestion des Actions Utilisateur --- /** * Configure le listener pour détecter les swipes sur le plateau de jeu. @@ -258,131 +252,105 @@ public class MainActivity extends AppCompatActivity { /** * Traite un geste de swipe de l'utilisateur sur le plateau de jeu. - * 1. Tente d'effectuer le mouvement dans l'objet Game. - * 2. Si le mouvement a modifié le plateau (boardChanged == true) : - * - Met à jour les statistiques (mouvements, fusions, score, etc.). - * - Ajoute une nouvelle tuile aléatoire. - * - Met à jour l'affichage (UI). - * 3. Vérifie l'état du jeu (gagné ou perdu) APRÈS la tentative de mouvement, - * que le plateau ait changé ou non. C'est la correction clé. - * 4. Affiche les boîtes de dialogue appropriées (victoire, défaite). - * - * @param direction La direction du swipe détecté (UP, DOWN, LEFT, RIGHT). + * Met à jour le jeu, les statistiques et l'UI, et vérifie les conditions de fin de partie. + * @param direction La direction du swipe détecté. */ private void handleSwipe(Direction direction) { - if (game == null || gameStats == null || currentGameState == GameFlowState.GAME_OVER) { - return; + return; // Ignore swipe si jeu terminé ou non initialisé } - int scoreBefore = game.getCurrentScore(); - boolean boardChanged = false; - - - + // Tente d'effectuer le mouvement dans l'objet Game switch (direction) { - case UP: - boardChanged = game.pushUp(); - break; - case DOWN: - boardChanged = game.pushDown(); - break; - case LEFT: - boardChanged = game.pushLeft(); - break; - case RIGHT: - boardChanged = game.pushRight(); - break; + case UP: boardChanged = game.pushUp(); break; + case DOWN: boardChanged = game.pushDown(); break; + case LEFT: boardChanged = game.pushLeft(); break; + case RIGHT: boardChanged = game.pushRight(); break; } - + // Si le mouvement a modifié le plateau if (boardChanged) { - gameStats.recordMove(); int scoreAfter = game.getCurrentScore(); int scoreDelta = scoreAfter - scoreBefore; if (scoreDelta > 0) { - - gameStats.recordMerge(1); - + gameStats.recordMerge(1); // Simplification: compte comme 1 fusion si score augmente if (scoreAfter > game.getHighestScore()) { game.setHighestScore(scoreAfter); - gameStats.setHighestScore(scoreAfter); + gameStats.setHighestScore(scoreAfter); // Met à jour aussi dans GameStats pour sauvegarde } } - gameStats.updateHighestTile(game.getHighestTileValue()); - - - game.addNewTile(); - - - updateUI(); - + game.addNewTile(); // Ajoute une nouvelle tuile + updateUI(); // Rafraîchit l'affichage } - - - + // Vérifie l'état final après le mouvement (même si boardChanged est false) if (currentGameState != GameFlowState.GAME_OVER) { - - - if (game.isGameWon() && currentGameState == GameFlowState.PLAYING) { currentGameState = GameFlowState.WON_DIALOG_SHOWN; - long timeTaken = System.currentTimeMillis() - gameStats.getCurrentGameStartTimeMs(); gameStats.recordWin(timeTaken); - showGameWonKeepPlayingDialog(); - - - } else if (game.isGameOver()) { currentGameState = GameFlowState.GAME_OVER; - long timeTaken = System.currentTimeMillis() - gameStats.getCurrentGameStartTimeMs(); gameStats.recordLoss(); - gameStats.endGame(timeTaken); - + gameStats.endGame(timeTaken); // Finalise temps, etc. showGameOverDialog(); - - if (!boardChanged) { - updateUI(); - } + // Met à jour l'UI pour afficher le score final si Game Over atteint sans mouvement (rare) + if (!boardChanged) updateUI(); } - } } - enum Direction { UP, DOWN, LEFT, RIGHT } + /** Énumération pour les directions de swipe. */ + private enum Direction { UP, DOWN, LEFT, RIGHT } + + // --- Dialogues --- /** * Affiche la boîte de dialogue demandant confirmation avant de redémarrer. */ private void showRestartConfirmationDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); - LayoutInflater inflater = getLayoutInflater(); View dialogView = inflater.inflate(R.layout.dialog_restart_confirm, null); - builder.setView(dialogView); Button cancelButton = dialogView.findViewById(R.id.dialogCancelButton); Button confirmButton = dialogView.findViewById(R.id.dialogConfirmButton); - final AlertDialog dialog = builder.create(); cancelButton.setOnClickListener(v -> dialog.dismiss()); - confirmButton.setOnClickListener(v -> { dialog.dismiss(); startNewGame(); }); dialog.show(); + LayoutInflater inflater = getLayoutInflater(); + View dialogView = inflater.inflate(R.layout.dialog_restart_confirm, null); + builder.setView(dialogView); + Button cancelButton = dialogView.findViewById(R.id.dialogCancelButton); + Button confirmButton = dialogView.findViewById(R.id.dialogConfirmButton); + final AlertDialog dialog = builder.create(); + cancelButton.setOnClickListener(v -> dialog.dismiss()); + confirmButton.setOnClickListener(v -> { dialog.dismiss(); startNewGame(); }); + dialog.show(); } /** - * Démarre une nouvelle partie : initialise GameStats pour une nouvelle session, - * crée un nouvel objet Game, synchronise le meilleur score et met à jour l'UI. + * Démarre une nouvelle partie logiquement et rafraîchit l'UI. + * Réinitialise les stats de la partie en cours via `gameStats.startGame()`. + * Ferme le panneau de statistiques s'il est ouvert. */ private void startNewGame() { - gameStats.startGame(); - game = new Game(); - game.setHighestScore(gameStats.getOverallHighScore()); - currentGameState = GameFlowState.PLAYING; - updateUI(); + if (gameStats == null) gameStats = new GameStats(this); // Précaution si initialisation a échoué avant + gameStats.startGame(); // Réinitialise stats de partie (temps, mouvements, etc.) + game = new Game(); // Crée un nouveau jeu logique + game.setHighestScore(gameStats.getOverallHighScore()); // Applique le meilleur score global au nouveau jeu + currentGameState = GameFlowState.PLAYING; // Définit l'état à JOUER + + // Ferme le panneau de statistiques s'il était ouvert + if (statisticsVisible) { + toggleStatistics(); // Utilise la méthode existante pour masquer proprement + } + // Assure que le bouton multijoueur est visible (pourrait être masqué par les stats) + multiplayerButton.setVisibility(View.VISIBLE); + + updateUI(); // Met à jour l'affichage (plateau, scores) } + /** * Affiche la boîte de dialogue quand 2048 est atteint, en utilisant un layout personnalisé. * Propose de continuer à jouer ou de commencer une nouvelle partie. @@ -394,22 +362,18 @@ public class MainActivity extends AppCompatActivity { builder.setView(dialogView); builder.setCancelable(false); - Button keepPlayingButton = dialogView.findViewById(R.id.dialogKeepPlayingButton); Button newGameButton = dialogView.findViewById(R.id.dialogNewGameButtonWon); - final AlertDialog dialog = builder.create(); keepPlayingButton.setOnClickListener(v -> { - + // L'état est déjà WON_DIALOG_SHOWN, on ne fait rien de spécial, le jeu continue. dialog.dismiss(); }); - newGameButton.setOnClickListener(v -> { dialog.dismiss(); startNewGame(); }); - dialog.show(); } @@ -424,31 +388,102 @@ public class MainActivity extends AppCompatActivity { builder.setView(dialogView); builder.setCancelable(false); - TextView messageTextView = dialogView.findViewById(R.id.dialogMessageGameOver); Button newGameButton = dialogView.findViewById(R.id.dialogNewGameButtonGameOver); Button quitButton = dialogView.findViewById(R.id.dialogQuitButtonGameOver); - - messageTextView.setText(getString(R.string.game_over_message, game.getCurrentScore())); - final AlertDialog dialog = builder.create(); newGameButton.setOnClickListener(v -> { dialog.dismiss(); startNewGame(); }); - quitButton.setOnClickListener(v -> { dialog.dismiss(); - finish(); + finish(); // Ferme l'application }); - dialog.show(); } + // --- Menu Principal --- + + /** + * Affiche la boîte de dialogue du menu principal en utilisant un layout personnalisé. + * Attache les listeners aux boutons pour déclencher les actions correspondantes. + */ + private void showMenu() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + LayoutInflater inflater = getLayoutInflater(); + View dialogView = inflater.inflate(R.layout.dialog_main_menu, null); // Gonfle le layout personnalisé + builder.setView(dialogView); + builder.setCancelable(true); + + // Récupère les boutons du layout personnalisé + Button howToPlayButton = dialogView.findViewById(R.id.menuButtonHowToPlay); + Button settingsButton = dialogView.findViewById(R.id.menuButtonSettings); + Button aboutButton = dialogView.findViewById(R.id.menuButtonAbout); + Button returnButton = dialogView.findViewById(R.id.menuButtonReturn); + + final AlertDialog dialog = builder.create(); + + // Attache les listeners aux boutons + howToPlayButton.setOnClickListener(v -> { + dialog.dismiss(); // Ferme le menu + showHowToPlayDialog(); // Ouvre la dialogue "Comment Jouer" + }); + + settingsButton.setOnClickListener(v -> { + dialog.dismiss(); // Ferme le menu + showSettingsDialog(); // Ouvre la dialogue placeholder "Paramètres" + }); + + aboutButton.setOnClickListener(v -> { + dialog.dismiss(); // Ferme le menu + showAboutDialog(); // Ouvre la dialogue "À Propos" + }); + + returnButton.setOnClickListener(v -> { + dialog.dismiss(); // Ferme simplement le menu + }); + + dialog.show(); // Affiche la boîte de dialogue + } + + /** + * Affiche une boîte de dialogue simple expliquant les règles du jeu. + */ + private void showHowToPlayDialog() { + new AlertDialog.Builder(this) + .setTitle(R.string.how_to_play_title) + .setMessage(R.string.how_to_play_instructions) + .setPositiveButton(R.string.ok, (dialog, which) -> dialog.dismiss()) + .show(); + } + + /** + * Affiche une boîte de dialogue placeholder pour les paramètres. + */ + private void showSettingsDialog() { + new AlertDialog.Builder(this) + .setTitle(R.string.settings_title) + .setMessage(R.string.settings_message) // Message placeholder + .setPositiveButton(R.string.ok, (dialog, which) -> dialog.dismiss()) + .show(); + } + + /** + * Affiche une boîte de dialogue simple avec les informations "À Propos". + */ + private void showAboutDialog() { + new AlertDialog.Builder(this) + .setTitle(R.string.about_title) + .setMessage(R.string.about_message) // Pensez à personnaliser ceci dans strings.xml + .setPositiveButton(R.string.ok, (dialog, which) -> dialog.dismiss()) + .show(); + } + // --- Gestion Stats UI --- /** * Affiche ou masque le panneau de statistiques. @@ -457,20 +492,20 @@ public class MainActivity extends AppCompatActivity { private void toggleStatistics() { statisticsVisible = !statisticsVisible; if (statisticsVisible) { - if (inflatedStatsView == null) { + if (inflatedStatsView == null) { // Gonfle si pas encore fait inflatedStatsView = statisticsViewStub.inflate(); - + // Attache listener au bouton Back une fois la vue gonflée Button backButton = inflatedStatsView.findViewById(R.id.backButton); - backButton.setOnClickListener(v -> toggleStatistics()); + backButton.setOnClickListener(v -> toggleStatistics()); // Recliquer sur Back re-appelle toggle } - updateStatisticsTextViews(); - inflatedStatsView.setVisibility(View.VISIBLE); - multiplayerButton.setVisibility(View.GONE); + updateStatisticsTextViews(); // Remplit les champs avec les données actuelles + inflatedStatsView.setVisibility(View.VISIBLE); // Affiche + multiplayerButton.setVisibility(View.GONE); // Masque bouton multi } else { - if (inflatedStatsView != null) { + if (inflatedStatsView != null) { // Masque si la vue existe inflatedStatsView.setVisibility(View.GONE); } - multiplayerButton.setVisibility(View.VISIBLE); + multiplayerButton.setVisibility(View.VISIBLE); // Réaffiche bouton multi } } @@ -481,7 +516,7 @@ public class MainActivity extends AppCompatActivity { private void updateStatisticsTextViews() { if (inflatedStatsView == null || gameStats == null) return; - + // Récupération des TextViews dans la vue gonflée TextView highScoreStatsLabel = inflatedStatsView.findViewById(R.id.high_score_stats_label); TextView totalGamesPlayedLabel = inflatedStatsView.findViewById(R.id.total_games_played_label); TextView totalGamesStartedLabel = inflatedStatsView.findViewById(R.id.total_games_started_label); @@ -502,12 +537,12 @@ public class MainActivity extends AppCompatActivity { TextView multiplayerWinRateLabel = inflatedStatsView.findViewById(R.id.multiplayer_win_rate_label); TextView multiplayerBestWinningStreakLabel = inflatedStatsView.findViewById(R.id.multiplayer_best_winning_streak_label); TextView multiplayerAverageScoreLabel = inflatedStatsView.findViewById(R.id.multiplayer_average_score_label); - TextView averageTimePerGameMultiLabel = inflatedStatsView.findViewById(R.id.average_time_per_game_label); + TextView averageTimePerGameMultiLabel = inflatedStatsView.findViewById(R.id.average_time_per_game_label); // Potentiel ID dupliqué dans layout? TextView totalMultiplayerLossesLabel = inflatedStatsView.findViewById(R.id.total_multiplayer_losses_label); TextView multiplayerHighScoreLabel = inflatedStatsView.findViewById(R.id.multiplayer_high_score_label); TextView mergesThisGameLabel = inflatedStatsView.findViewById(R.id.merges_this_game); - + // MAJ textes avec getters de gameStats highScoreStatsLabel.setText(getString(R.string.high_score_stats, gameStats.getOverallHighScore())); totalGamesPlayedLabel.setText(getString(R.string.total_games_played, gameStats.getTotalGamesPlayed())); totalGamesStartedLabel.setText(getString(R.string.total_games_started, gameStats.getTotalGamesStarted())); @@ -525,30 +560,29 @@ public class MainActivity extends AppCompatActivity { totalMultiplayerLossesLabel.setText(getString(R.string.total_multiplayer_losses, gameStats.getTotalMultiplayerLosses())); multiplayerHighScoreLabel.setText(getString(R.string.multiplayer_high_score, gameStats.getMultiplayerHighestScore())); - + // Calculs Pourcentages String winPercentage = (gameStats.getTotalGamesStarted() > 0) ? String.format("%.2f%%", ((double) gameStats.getNumberOfTimesObjectiveReached() / gameStats.getTotalGamesStarted()) * 100) : "N/A"; winPercentageLabel.setText(getString(R.string.win_percentage, winPercentage)); String multiplayerWinRate = (gameStats.getMultiplayerGamesPlayed() > 0) ? String.format("%.2f%%", ((double) gameStats.getMultiplayerGamesWon() / gameStats.getMultiplayerGamesPlayed()) * 100) : "N/A"; multiplayerWinRateLabel.setText(getString(R.string.multiplayer_win_rate, multiplayerWinRate)); - + // Calculs Temps totalPlayTimeLabel.setText(getString(R.string.total_play_time, GameStats.formatTime(gameStats.getTotalPlayTimeMs()))); - long currentDuration = (game != null && !game.isGameOver() && !game.isGameWon() && gameStats != null) ? System.currentTimeMillis() - gameStats.getCurrentGameStartTimeMs() : 0; - currentGameTimeLabel.setText(getString(R.string.current_game_time, GameStats.formatTime(currentDuration))); + // Calcule le temps de la partie en cours seulement si elle n'est pas finie + long currentDurationMs = 0; + if (game != null && gameStats != null && currentGameState == GameFlowState.PLAYING && gameStats.getCurrentGameStartTimeMs() > 0) { + currentDurationMs = System.currentTimeMillis() - gameStats.getCurrentGameStartTimeMs(); + } + currentGameTimeLabel.setText(getString(R.string.current_game_time, GameStats.formatTime(currentDurationMs))); + averageGameTimeLabel.setText(getString(R.string.average_time_per_game, GameStats.formatTime(gameStats.getAverageGameTimeMs()))); - averageTimePerGameMultiLabel.setText(getString(R.string.average_time_per_game_label, GameStats.formatTime(gameStats.getMultiplayerAverageTimeMs()))); + averageTimePerGameMultiLabel.setText(getString(R.string.average_time_per_game_label, GameStats.formatTime(gameStats.getMultiplayerAverageTimeMs()))); // Assurez-vous que l'ID R.string.average_time_per_game_label est correct bestWinningTimeLabel.setText(getString(R.string.best_winning_time, (gameStats.getBestWinningTimeMs() != Long.MAX_VALUE) ? GameStats.formatTime(gameStats.getBestWinningTimeMs()) : "N/A")); worstWinningTimeLabel.setText(getString(R.string.worst_winning_time, (gameStats.getWorstWinningTimeMs() != 0) ? GameStats.formatTime(gameStats.getWorstWinningTimeMs()) : "N/A")); } - - /** Affiche un dialogue placeholder pour le menu. */ - private void showMenu() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("Menu").setMessage("Fonctionnalité de menu à venir !").setPositiveButton("OK", null); - builder.create().show(); - } + // --- Placeholders Multi --- /** Affiche un dialogue placeholder pour le multijoueur. */ private void showMultiplayerScreen() { @@ -557,45 +591,60 @@ public class MainActivity extends AppCompatActivity { builder.create().show(); } - + // --- Sauvegarde / Chargement --- /** Sauvegarde l'état du jeu et le meilleur score via SharedPreferences. */ private void saveGame() { SharedPreferences.Editor editor = preferences.edit(); if (game != null) { - editor.putString(GAME_STATE_KEY, game.toString()); + editor.putString(GAME_STATE_KEY, game.toString()); // Sérialise Game (plateau + score courant) + // Le meilleur score est géré et sauvegardé par GameStats, mais on le sauve aussi ici pour la synchro au chargement editor.putInt(HIGH_SCORE_KEY, game.getHighestScore()); } else { - editor.remove(GAME_STATE_KEY); + editor.remove(GAME_STATE_KEY); // Optionnel: nettoyer si pas de jeu } - editor.apply(); + editor.apply(); // Utilise apply() pour une sauvegarde asynchrone } /** Charge l'état du jeu depuis SharedPreferences et synchronise le meilleur score. */ private void loadGame() { String gameStateString = preferences.getString(GAME_STATE_KEY, null); + // Charge le meilleur score depuis les préférences (sera aussi chargé par GameStats mais on l'utilise ici pour Game) int savedHighScore = preferences.getInt(HIGH_SCORE_KEY, 0); - if (gameStats != null) { - gameStats.setHighestScore(savedHighScore); + // Assure que GameStats charge son état (y compris le HS global) + if (gameStats == null) { gameStats = new GameStats(this); } // Précaution + gameStats.loadStats(); // Charge explicitement les stats (ce qui devrait inclure le HS global) + // S'assure que le HS chargé par gameStats est cohérent avec celui des prefs directes + if (savedHighScore > gameStats.getOverallHighScore()) { + gameStats.setHighestScore(savedHighScore); // Assure que GameStats a au moins le HS trouvé ici + } else { + savedHighScore = gameStats.getOverallHighScore(); // Utilise le HS de GameStats s'il est plus grand } + + Game loadedGame = null; if (gameStateString != null) { - game = Game.deserialize(gameStateString); - if (game != null) { - game.setHighestScore(savedHighScore); + loadedGame = Game.deserialize(gameStateString); + } - if (game.isGameOver()) currentGameState = GameFlowState.GAME_OVER; - else if (game.isGameWon()) currentGameState = GameFlowState.WON_DIALOG_SHOWN; - else currentGameState = GameFlowState.PLAYING; - } else { game = null; } - } else { game = null; } - - if (game == null) { - game = new Game(); - game.setHighestScore(savedHighScore); - currentGameState = GameFlowState.PLAYING; + if (loadedGame != null) { + game = loadedGame; + game.setHighestScore(savedHighScore); // Applique le HS synchronisé + // Détermine l'état basé sur le jeu chargé + if (game.isGameOver()) { + currentGameState = GameFlowState.GAME_OVER; + } else if (game.isGameWon()) { + // Si on charge une partie déjà gagnée, on considère qu'on a déjà vu la dialog + currentGameState = GameFlowState.WON_DIALOG_SHOWN; + } else { + currentGameState = GameFlowState.PLAYING; + // Le timer sera (re)démarré dans onResume si l'état est PLAYING + } + } else { + // Pas de sauvegarde valide ou erreur de désérialisation -> Commence une nouvelle partie implicitement + game = null; // Sera géré par l'appel à startNewGame dans initializeGameAndStats } } -} \ No newline at end of file +} // Fin MainActivity \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_main_menu.xml b/app/src/main/res/layout/dialog_main_menu.xml new file mode 100644 index 0000000..bdd97c0 --- /dev/null +++ b/app/src/main/res/layout/dialog_main_menu.xml @@ -0,0 +1,63 @@ + + + + + +