Feat: Implémentation menu principal via AlertDialog
- Ajout des chaînes de caractères pour le menu et ses options dans strings.xml. - Modification de MainActivity : - Implémentation de showMenu() pour créer et afficher la liste d'options grâce à un layout personnalisé (dialog_main_menu.xml). - Ajout de showHowToPlayDialog() affichant les règles de base. - Ajout de showAboutDialog() affichant des informations sur l'app. - Ajout de showSettingsDialog() comme placeholder pour les futurs paramètres.
This commit is contained in:
parent
21ff127536
commit
2a782950cd
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // Fin MainActivity
|
63
app/src/main/res/layout/dialog_main_menu.xml
Normal file
63
app/src/main/res/layout/dialog_main_menu.xml
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp"
|
||||
android:background="@drawable/dialog_background">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dialogTitleMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/menu_title"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text_tile_low"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/menuButtonHowToPlay"
|
||||
style="@style/ButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp" android:layout_marginEnd="0dp"
|
||||
android:layout_marginTop="4dp" android:layout_marginBottom="4dp"
|
||||
android:text="@string/menu_option_how_to_play" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/menuButtonSettings"
|
||||
style="@style/ButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp" android:layout_marginEnd="0dp"
|
||||
android:layout_marginTop="4dp" android:layout_marginBottom="4dp"
|
||||
android:text="@string/menu_option_settings" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/menuButtonAbout"
|
||||
style="@style/ButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp" android:layout_marginEnd="0dp"
|
||||
android:layout_marginTop="4dp" android:layout_marginBottom="4dp"
|
||||
android:text="@string/menu_option_about" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/game_board_background"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/menuButtonReturn"
|
||||
style="@style/ButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp" android:layout_marginEnd="0dp"
|
||||
android:layout_marginTop="4dp" android:layout_marginBottom="0dp"
|
||||
android:text="@string/menu_option_return" />
|
||||
|
||||
</LinearLayout>
|
@ -51,4 +51,16 @@
|
||||
<string name="game_over_title">Game over!</string>
|
||||
<string name="game_over_message">No move possible.\nFinal score: %d</string>
|
||||
<string name="quit">To leave</string>
|
||||
<string name="menu_title">Main Menu</string>
|
||||
<string name="menu_option_how_to_play">How to Play</string>
|
||||
<string name="menu_option_settings">Settings</string>
|
||||
<string name="menu_option_about">About</string>
|
||||
<string name="menu_option_return">Back</string>
|
||||
<string name="how_to_play_title">How to Play</string>
|
||||
<string name="how_to_play_instructions">Swipe the screen (Up, Down, Left, Right) to move all the tiles.\n\nWhen two tiles with the same number touch, they merge into one!\n\nReach the 2048 tile to win.\n\nThe game is over if the board is full and no moves are possible.</string>
|
||||
<string name="about_title">About Best 2048</string>
|
||||
<string name="about_message">Version: 1.0 (University Project)\nDeveloped by: La Legion de Muyue\n(Leader: Muyue, Members: 2 others)\n\nBased on the popular game 2048.</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="settings_title">Settings</string>
|
||||
<string name="settings_message">Settings screen to be implemented.</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user