feat: Ajout du hub de jeux et nouveau mode 89++
Nouvelles fonctionnalités: - Hub de sélection de jeux avec cartes animées - Refonte complète de Boidelo Classic avec nouvelle architecture - Nouveau mode 89++ : assistant de jeu avec timer et défis - Système de joueurs dynamique avec ajout/suppression - Interface moderne et cohérente entre les jeux Améliorations: - Système de défis avec pause pendant l'affichage - Gestion des gorgées avec slider (1-8) - Statistiques de joueurs en temps réel - Bouton retour fonctionnel avec gestion de la notch 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+914
@@ -0,0 +1,914 @@
|
||||
package com.example.boidelov3.games.boideloclassic;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import com.google.android.material.appbar.MaterialToolbar;
|
||||
import com.example.boidelov3.BoideloAnimationUtils;
|
||||
import com.example.boidelov3.EndGameActivity;
|
||||
import com.example.boidelov3.R;
|
||||
import com.example.boidelov3.data.PlayerStats;
|
||||
import com.example.boidelov3.data.QuestionCategory;
|
||||
import com.example.boidelov3.data.QuestionRepository;
|
||||
import com.example.boidelov3.Question;
|
||||
import com.example.boidelov3.Questions;
|
||||
import com.example.boidelov3.utils.ErrorHandler;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* BoideloClassicGameActivity - Activité principale du jeu Boidelo Classic
|
||||
*
|
||||
* Cette activité gère le déroulement du jeu avec les questions, les défis,
|
||||
* les manches et les statistiques des joueurs.
|
||||
*
|
||||
* C'est une version refactorisée de l'ancienne Jeux.java
|
||||
*/
|
||||
public class BoideloClassicGameActivity extends AppCompatActivity {
|
||||
|
||||
// UI Components
|
||||
private TextView questionTextView;
|
||||
private TextView progressTextView;
|
||||
private TextView mancheCounterTextView;
|
||||
private TextView mancheQuestionText;
|
||||
private ProgressBar progressBar;
|
||||
private View suivantButton;
|
||||
private View skipButton;
|
||||
private View questionIndicator;
|
||||
private View indicatorIcon;
|
||||
private TextView indicatorText;
|
||||
private View rootLayout;
|
||||
|
||||
// Bulle pour les défis
|
||||
private PopupWindow defiBubble;
|
||||
|
||||
// Data
|
||||
private List<Question> questions;
|
||||
private ArrayList<String> toutlesjoueurs;
|
||||
private List<Question> questionsAvecManches;
|
||||
private Map<String, PlayerStats> playerStatsMap;
|
||||
|
||||
// Services
|
||||
// private SoundManager soundManager; // TODO: Fix SoundManager constructor
|
||||
// private GameEngine gameEngine; // TODO: Fix GameEngine
|
||||
private QuestionRepository questionRepository;
|
||||
|
||||
// Random
|
||||
private final Random random = new Random();
|
||||
|
||||
// Game Settings
|
||||
private int nombreQuestions = 20;
|
||||
private int ajoutGorgees = 1;
|
||||
private boolean openAI = false;
|
||||
private int ratiOpenai = 5;
|
||||
private String keyOpenai = "";
|
||||
private int durationDefis = 10;
|
||||
|
||||
// Game State
|
||||
private int currentQuestionIndex = 0;
|
||||
private int totalQuestionsAsked = 0;
|
||||
private String currentQuestionText = "";
|
||||
private boolean isMancheActive = false;
|
||||
private boolean isFinishingGame = false;
|
||||
private int questionsSinceLastAI = 0;
|
||||
|
||||
// Constants
|
||||
private static final int MIN_DEFI_ROUNDS = 3;
|
||||
private static final int MAX_DEFI_ROUNDS_RANDOM = 15;
|
||||
private static final int MIN_MANCHES_COUNT = 5;
|
||||
private static final int PREGENERATED_AI_QUESTIONS = 10;
|
||||
private static final int MIN_AI_QUESTION_STOCK = 3;
|
||||
private static final int MIN_AI_GORGEE = 1;
|
||||
private static final int MAX_AI_GORGEE_ADDITIONAL = 4;
|
||||
private static final int RANDOM_PLAYER_SELECTION_COUNT = 3;
|
||||
private static final int TWO_PLAYERS = 2;
|
||||
private static final int ONE_PLAYER = 1;
|
||||
private static final int ANIMATION_DURATION_SHORT_MS = 300;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_boidelo_classic_game);
|
||||
|
||||
// Configure la toolbar avec un bouton retour
|
||||
MaterialToolbar toolbar = findViewById(R.id.toolbar);
|
||||
toolbar.setNavigationOnClickListener(v -> finish());
|
||||
|
||||
// Récupère les joueurs depuis l'intent
|
||||
Intent intent = getIntent();
|
||||
toutlesjoueurs = intent.getStringArrayListExtra("PLAYERS");
|
||||
|
||||
initViews();
|
||||
initServices();
|
||||
loadQuestions();
|
||||
initializePlayerStats();
|
||||
setupProgressBar();
|
||||
setupButtonListeners();
|
||||
|
||||
// Affiche la première question
|
||||
displayNewQuestion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise les vues de l'activité
|
||||
*/
|
||||
private void initViews() {
|
||||
questionTextView = findViewById(R.id.questionTextView);
|
||||
progressTextView = findViewById(R.id.progressTextView);
|
||||
mancheCounterTextView = findViewById(R.id.mancheCounterTextView);
|
||||
mancheQuestionText = findViewById(R.id.mancheQuestionText);
|
||||
progressBar = findViewById(R.id.progressBar);
|
||||
suivantButton = findViewById(R.id.suivantButton);
|
||||
skipButton = findViewById(R.id.skipButton);
|
||||
questionIndicator = findViewById(R.id.questionIndicator);
|
||||
indicatorIcon = findViewById(R.id.indicatorIcon);
|
||||
indicatorText = findViewById(R.id.indicatorText);
|
||||
rootLayout = findViewById(R.id.rootLayout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise les services
|
||||
*/
|
||||
private void initServices() {
|
||||
// soundManager = new SoundManager(this); // TODO: Fix SoundManager
|
||||
// gameEngine = new GameEngine(); // TODO: Fix GameEngine
|
||||
questionRepository = new QuestionRepository(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge les questions depuis le repository
|
||||
*/
|
||||
private void loadQuestions() {
|
||||
com.example.boidelov3.data.Result<Questions, ?> result = questionRepository.loadQuestions();
|
||||
if (result.isSuccess()) {
|
||||
Questions q = result.getData();
|
||||
questions = q != null && q.getQuestions() != null ? q.getQuestions() : new ArrayList<>();
|
||||
} else {
|
||||
questions = new ArrayList<>();
|
||||
}
|
||||
questionsAvecManches = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise les statistiques des joueurs
|
||||
*/
|
||||
private void initializePlayerStats() {
|
||||
playerStatsMap = new HashMap<>();
|
||||
for (String player : toutlesjoueurs) {
|
||||
playerStatsMap.put(player, new PlayerStats(player));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure la barre de progression
|
||||
*/
|
||||
private void setupProgressBar() {
|
||||
progressBar.setMax(nombreQuestions);
|
||||
progressBar.setProgress(0);
|
||||
updateProgressText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure les écouteurs de boutons
|
||||
*/
|
||||
private void setupButtonListeners() {
|
||||
suivantButton.setOnClickListener(v -> OnClickButton1(null));
|
||||
skipButton.setOnClickListener(v -> onSkipClick(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le texte de progression
|
||||
*/
|
||||
private void updateProgressText() {
|
||||
progressTextView.setText(String.format("%d / %d", currentQuestionIndex, nombreQuestions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour la barre de progression
|
||||
*/
|
||||
private void updateProgressBar() {
|
||||
progressBar.setProgress(currentQuestionIndex);
|
||||
updateProgressText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour la question affichée
|
||||
*/
|
||||
private void updateQuestion() {
|
||||
// Si un défi est en cours, diminuer le compteur
|
||||
if (!questionsAvecManches.isEmpty()) {
|
||||
boolean defiFinished = processActiveManches();
|
||||
if (defiFinished) {
|
||||
// Le défi vient de se terminer, ne pas afficher de nouvelle question
|
||||
return;
|
||||
}
|
||||
// Le défi continue, afficher une nouvelle question normalement
|
||||
}
|
||||
|
||||
// Sinon, vérifier si le jeu doit se terminer
|
||||
if (currentQuestionIndex >= nombreQuestions || shouldEndGame()) {
|
||||
handleGameEnd();
|
||||
} else {
|
||||
displayNewQuestion();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le jeu doit se terminer
|
||||
*/
|
||||
private boolean shouldEndGame() {
|
||||
return currentQuestionIndex >= nombreQuestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère la fin du jeu
|
||||
*/
|
||||
private void handleGameEnd() {
|
||||
isFinishingGame = true;
|
||||
|
||||
// Prépare les stats pour l'activité de fin
|
||||
Intent intent = new Intent(this, EndGameActivity.class);
|
||||
intent.putExtra("PLAYER_STATS", new ArrayList<>(playerStatsMap.values()));
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Traite les manches actives et retourne true si le défi vient de se terminer
|
||||
*/
|
||||
private boolean processActiveManches() {
|
||||
if (questionsAvecManches.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Question mancheQuestion = questionsAvecManches.get(0);
|
||||
int manchesRestantes = mancheQuestion.getManchesRestantes();
|
||||
|
||||
if (manchesRestantes > 0) {
|
||||
// Diminuer le compteur
|
||||
mancheQuestion.setManchesRestantes(manchesRestantes - 1);
|
||||
|
||||
if (manchesRestantes - 1 == 0) {
|
||||
// Fin du défi - sauvegarder la question avant de vider
|
||||
Question finishedDefi = mancheQuestion;
|
||||
questionsAvecManches.clear();
|
||||
|
||||
// Cacher le compteur de manches et le bouton skip
|
||||
mancheCounterTextView.setVisibility(View.GONE);
|
||||
mancheQuestionText.setVisibility(View.GONE);
|
||||
updateSkipButtonVisibility();
|
||||
|
||||
showFinalMancheEndMessage(finishedDefi);
|
||||
return true; // Le défi vient de se terminer
|
||||
} else {
|
||||
// Continuer le défi - juste mettre à jour le compteur
|
||||
updateMancheCounter(manchesRestantes - 1);
|
||||
return false; // Le défi continue, on affichera une nouvelle question
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche le message final de fin de manche
|
||||
*/
|
||||
private void showFinalMancheEndMessage(Question finishedDefi) {
|
||||
// Afficher la question complète du défi qui vient de se terminer
|
||||
String questionText = finishedDefi.getQuestion();
|
||||
String fullMessage = "<b>🏆 Fin du défi !</b><br><br>" + questionText;
|
||||
displayQuestionText(fullMessage);
|
||||
|
||||
// Masquer le compteur de manches
|
||||
mancheCounterTextView.setVisibility(View.GONE);
|
||||
|
||||
// Terminer après un délai plus long pour laisser le temps de lire
|
||||
rootLayout.postDelayed(() -> {
|
||||
// Continuer avec une nouvelle question ou finir le jeu
|
||||
updateQuestion();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche une nouvelle question
|
||||
*/
|
||||
private void displayNewQuestion() {
|
||||
if (questions == null || questions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Question question = selectRandomQuestion();
|
||||
currentQuestionText = question.getQuestion();
|
||||
currentQuestionIndex++;
|
||||
|
||||
// Traiter le texte de la question (remplace J1, variante, manches, etc.)
|
||||
processQuestionText(question);
|
||||
|
||||
// Afficher la question avec couleurs et emojis
|
||||
displayQuestion(question);
|
||||
updateProgressBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sélectionne une question aléatoire en évitant les doublons de défis
|
||||
*/
|
||||
private Question selectRandomQuestion() {
|
||||
boolean hasActiveDefi = !questionsAvecManches.isEmpty();
|
||||
int maxAttempts = questions.size(); // Éviter boucle infinie
|
||||
int attempts = 0;
|
||||
|
||||
while (attempts < maxAttempts) {
|
||||
Question question = questions.get(random.nextInt(questions.size()));
|
||||
|
||||
// Si un défi est en cours, éviter les questions avec <manches>
|
||||
if (hasActiveDefi && question.getQuestion().contains("<manches>")) {
|
||||
attempts++;
|
||||
continue;
|
||||
}
|
||||
|
||||
return question;
|
||||
}
|
||||
|
||||
// Si on ne trouve pas de question valide, retourner une question aléatoire
|
||||
return questions.get(random.nextInt(questions.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche la notification de fin de manche
|
||||
*/
|
||||
private void showMancheEndNotification() {
|
||||
// TODO: Implémenter la notification de fin de manche
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche une question de manche en petit
|
||||
*/
|
||||
private void displayMancheQuestionSmall(String question) {
|
||||
mancheQuestionText.setText(question);
|
||||
mancheQuestionText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche une question de manche
|
||||
*/
|
||||
private void displayMancheQuestion(String question) {
|
||||
displayMancheQuestionSmall(question);
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le compteur de manches (affiché pendant un défi)
|
||||
*/
|
||||
private void updateMancheCounter(int manchesRestantes) {
|
||||
String tourWord = manchesRestantes <= 1 ? "tour" : "tours";
|
||||
String restantWord = manchesRestantes <= 1 ? "restant" : "restants";
|
||||
mancheCounterTextView.setText("Défis : " + manchesRestantes + " " + tourWord + " " + restantWord);
|
||||
mancheCounterTextView.setVisibility(View.VISIBLE);
|
||||
|
||||
// Afficher le bouton skip pendant un défi
|
||||
updateSkipButtonVisibility();
|
||||
|
||||
// Créer la bulle pour afficher le défi quand on maintient le clic
|
||||
mancheCounterTextView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (questionsAvecManches.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Quand on appuie, afficher la bulle
|
||||
showDefiBubble(v);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
// Quand on relâche, cacher la bulle
|
||||
hideDefiBubble();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche la bulle avec la question du défi
|
||||
*/
|
||||
private void showDefiBubble(View anchorView) {
|
||||
if (questionsAvecManches.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Question defi = questionsAvecManches.get(0);
|
||||
|
||||
// Créer le contenu de la bulle
|
||||
LinearLayout bubbleContent = new LinearLayout(this);
|
||||
bubbleContent.setOrientation(LinearLayout.VERTICAL);
|
||||
bubbleContent.setPadding(24, 16, 24, 16);
|
||||
bubbleContent.setBackgroundResource(R.drawable.bg_card);
|
||||
|
||||
TextView bubbleText = new TextView(this);
|
||||
bubbleText.setText(Html.fromHtml(defi.getQuestion(), Html.FROM_HTML_MODE_LEGACY));
|
||||
bubbleText.setTextColor(getColor(R.color.text_primary));
|
||||
bubbleText.setTextSize(16);
|
||||
bubbleContent.addView(bubbleText);
|
||||
|
||||
// Créer la PopupWindow
|
||||
defiBubble = new PopupWindow(bubbleContent, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
defiBubble.setOutsideTouchable(true);
|
||||
defiBubble.setFocusable(true);
|
||||
|
||||
// Afficher la bulle au-dessus du compteur (plus haut que le doigt)
|
||||
defiBubble.showAsDropDown(anchorView, 0, -150, Gravity.CENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache la bulle du défi
|
||||
*/
|
||||
private void hideDefiBubble() {
|
||||
if (defiBubble != null && defiBubble.isShowing()) {
|
||||
defiBubble.dismiss();
|
||||
defiBubble = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche une question
|
||||
*/
|
||||
private void displayQuestion(Question question) {
|
||||
displayQuestionText(question.getQuestion());
|
||||
handleMancheQuestionVisibility(question);
|
||||
applyCategoryStyle(question); // Applique la couleur de fond et l'emoji
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche le texte de la question (supporte le HTML pour le gras)
|
||||
*/
|
||||
private void displayQuestionText(String text) {
|
||||
questionTextView.setText(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère la visibilité de la question de manche
|
||||
*/
|
||||
private void handleMancheQuestionVisibility(Question question) {
|
||||
if (question.isManches()) {
|
||||
mancheQuestionText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mancheQuestionText.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applique le style de catégorie (couleur de fond et emojis)
|
||||
*/
|
||||
private void applyCategoryStyle(Question question) {
|
||||
QuestionCategory.Category category = QuestionCategory.detectCategory(question);
|
||||
|
||||
// Réinitialiser les indicateurs
|
||||
questionIndicator.setVisibility(View.GONE);
|
||||
|
||||
// Appliquer la couleur de fond
|
||||
int categoryColor = QuestionCategory.getColorForCategory(category);
|
||||
BoideloAnimationUtils.animateBackgroundColor(rootLayout, categoryColor, 300);
|
||||
|
||||
// N'afficher l'indicateur que si un défi n'est PAS en cours
|
||||
if (questionsAvecManches.isEmpty()) {
|
||||
String indicatorText = getCategoryQuestionIndicator(category, question);
|
||||
if (!indicatorText.isEmpty()) {
|
||||
showQuestionIndicatorWithEmoji(indicatorText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'emoji associé à une catégorie
|
||||
*/
|
||||
private String getCategoryEmoji(QuestionCategory.Category category) {
|
||||
switch (category) {
|
||||
case CIBLAGE:
|
||||
return "🎯";
|
||||
case CLASSEMENT:
|
||||
return "👑";
|
||||
case JUGEMENT:
|
||||
return "⚖️";
|
||||
case DUEL:
|
||||
return "🤝";
|
||||
case INTERACTIF:
|
||||
return "🎮";
|
||||
case DEFI_MANCHES:
|
||||
return "🔥";
|
||||
case VARIANTE:
|
||||
return "❓";
|
||||
case CALIENTE:
|
||||
return "😈";
|
||||
case VOTE:
|
||||
return "🗳️";
|
||||
case CLASSIQUE:
|
||||
default:
|
||||
return "⚪";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine le texte de l'indicateur en fonction du nombre de joueurs et de la catégorie
|
||||
*/
|
||||
private String getCategoryQuestionIndicator(QuestionCategory.Category category, Question question) {
|
||||
String categoryEmoji = getCategoryEmoji(category);
|
||||
String categoryName = QuestionCategory.getNameForCategory(category);
|
||||
int playerCount = toutlesjoueurs.size();
|
||||
|
||||
// Pour les questions spéciales, on affiche juste l'emoji et la catégorie
|
||||
if (category == QuestionCategory.Category.CALIENTE ||
|
||||
category == QuestionCategory.Category.DEFI_MANCHES ||
|
||||
category == QuestionCategory.Category.INTERACTIF) {
|
||||
return categoryEmoji + " " + categoryName;
|
||||
}
|
||||
|
||||
// Pour les autres, on affiche l'emoji + nombre de joueurs
|
||||
return categoryEmoji + " " + playerCount + " joueurs";
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche l'indicateur avec le texte spécifié et animation
|
||||
*/
|
||||
private void showQuestionIndicatorWithEmoji(String text) {
|
||||
indicatorText.setText(text);
|
||||
questionIndicator.setVisibility(View.VISIBLE);
|
||||
BoideloAnimationUtils.fadeIn(questionIndicator, 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine l'indicateur de nombre de joueurs
|
||||
*/
|
||||
private String determinePlayerCountIndicator(Question question) {
|
||||
return String.format("%d joueurs", toutlesjoueurs.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche l'indicateur de texte
|
||||
*/
|
||||
private void showIndicatorText(String text) {
|
||||
indicatorText.setText(text);
|
||||
questionIndicator.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche l'indicateur de question
|
||||
*/
|
||||
private void showQuestionIndicator(Question question) {
|
||||
showIndicatorText("❓ " + determinePlayerCountIndicator(question));
|
||||
}
|
||||
|
||||
/**
|
||||
* Passe la question
|
||||
*/
|
||||
private void skipQuestion() {
|
||||
updateQuestion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Termine le jeu
|
||||
*/
|
||||
private void endGame() {
|
||||
handleGameEnd();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// TRAITEMENT DU TEXTE DES QUESTIONS
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Traite le texte de la question pour remplacer tous les placeholders
|
||||
*/
|
||||
private void processQuestionText(Question question) {
|
||||
String questionText = question.getQuestion();
|
||||
|
||||
// Remplacer les variantes
|
||||
questionText = processVariantes(question, questionText);
|
||||
|
||||
// Gérer les manches
|
||||
questionText = processManches(question, questionText);
|
||||
|
||||
// Remplacer les joueurs et récupérer le nom du joueur J1 pour les stats
|
||||
PlayerSelectionResult playerResult = replacePlayers(questionText);
|
||||
questionText = playerResult.questionText;
|
||||
|
||||
// Ajouter les gorgées et tracker les stats
|
||||
GorgeeResult gorgeeResult = processGorgees(question, questionText, playerResult.playerCount);
|
||||
questionText = gorgeeResult.questionText;
|
||||
|
||||
// Mettre à jour les statistiques du joueur J1
|
||||
if (playerResult.j1Name != null) {
|
||||
updatePlayerStats(playerResult.j1Name, gorgeeResult);
|
||||
}
|
||||
|
||||
question.setQuestion(questionText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remplace les variantes dans la question
|
||||
*/
|
||||
private String processVariantes(Question question, String questionText) {
|
||||
if (question.getVariante() != null && !question.getVariante().isEmpty()) {
|
||||
String chosenVariante = question.getVariante().get(random.nextInt(question.getVariante().size()));
|
||||
questionText = questionText.replace("<variante>", chosenVariante);
|
||||
}
|
||||
return questionText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère les manches pour les défis
|
||||
*/
|
||||
private String processManches(Question question, String questionText) {
|
||||
if (questionText.contains("<manches>")) {
|
||||
int nbaleatoiremanches = calculateManchesCount();
|
||||
questionText = questionText.replace("<manches>", String.valueOf(nbaleatoiremanches));
|
||||
question.setManchesRestantes(nbaleatoiremanches);
|
||||
|
||||
// Définir le message de fin de défi
|
||||
String stopMessage = "Fin de défi!\n" + question.getQuestion();
|
||||
question.setArretMessageManche(stopMessage);
|
||||
|
||||
questionsAvecManches.add(question);
|
||||
|
||||
// Afficher le compteur de manches initial
|
||||
updateMancheCounter(nbaleatoiremanches);
|
||||
}
|
||||
return questionText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule le nombre de manches pour un défi
|
||||
*/
|
||||
private int calculateManchesCount() {
|
||||
int nbaleatoiremanches = random.nextInt(MAX_DEFI_ROUNDS_RANDOM) + MIN_DEFI_ROUNDS + durationDefis;
|
||||
return Math.max(nbaleatoiremanches, MIN_MANCHES_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Résultat du remplacement des joueurs
|
||||
*/
|
||||
private static class PlayerSelectionResult {
|
||||
String questionText;
|
||||
String j1Name;
|
||||
int playerCount;
|
||||
|
||||
PlayerSelectionResult(String questionText, String j1Name, int playerCount) {
|
||||
this.questionText = questionText;
|
||||
this.j1Name = j1Name;
|
||||
this.playerCount = playerCount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remplace les placeholders de joueurs dans la question
|
||||
*/
|
||||
private PlayerSelectionResult replacePlayers(String questionText) {
|
||||
boolean isJoueurs1 = questionText.contains("<J1>");
|
||||
boolean isJoueurs2 = questionText.contains("<J2>");
|
||||
boolean isJoueurs3 = questionText.contains("<J3>");
|
||||
|
||||
String j1Name = null;
|
||||
int playerCount = 0;
|
||||
|
||||
if (isJoueurs1 || isJoueurs2 || isJoueurs3) {
|
||||
List<String> aleatoirejoueurs = TroisJoueurAleatoire();
|
||||
|
||||
if (isJoueurs1 && isJoueurs2 && isJoueurs3 && aleatoirejoueurs.size() >= RANDOM_PLAYER_SELECTION_COUNT) {
|
||||
playerCount = 3;
|
||||
j1Name = aleatoirejoueurs.get(0);
|
||||
questionText = questionText.replace("<J1>", ErrorHandler.escapeHtml(j1Name));
|
||||
questionText = questionText.replace("<J2>", ErrorHandler.escapeHtml(aleatoirejoueurs.get(1)));
|
||||
questionText = questionText.replace("<J3>", ErrorHandler.escapeHtml(aleatoirejoueurs.get(2)));
|
||||
} else if (isJoueurs1 && isJoueurs2 && aleatoirejoueurs.size() >= TWO_PLAYERS) {
|
||||
playerCount = 2;
|
||||
j1Name = aleatoirejoueurs.get(0);
|
||||
questionText = questionText.replace("<J1>", ErrorHandler.escapeHtml(j1Name));
|
||||
questionText = questionText.replace("<J2>", ErrorHandler.escapeHtml(aleatoirejoueurs.get(1)));
|
||||
} else if (isJoueurs1 && aleatoirejoueurs.size() >= ONE_PLAYER) {
|
||||
playerCount = 1;
|
||||
j1Name = aleatoirejoueurs.get(0);
|
||||
questionText = questionText.replace("<J1>", ErrorHandler.escapeHtml(j1Name));
|
||||
}
|
||||
}
|
||||
|
||||
return new PlayerSelectionResult(questionText, j1Name, playerCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Résultat du traitement des gorgées
|
||||
*/
|
||||
private static class GorgeeResult {
|
||||
String questionText;
|
||||
int totalGorgees;
|
||||
boolean isBois;
|
||||
boolean isDistribue;
|
||||
|
||||
GorgeeResult(String questionText, int totalGorgees, boolean isBois, boolean isDistribue) {
|
||||
this.questionText = questionText;
|
||||
this.totalGorgees = totalGorgees;
|
||||
this.isBois = isBois;
|
||||
this.isDistribue = isDistribue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Résultat du choix d'action (bois/distribue)
|
||||
*/
|
||||
private static class ActionChoiceResult {
|
||||
String questionText;
|
||||
boolean isBois;
|
||||
boolean isDistribue;
|
||||
|
||||
ActionChoiceResult(String questionText, boolean isBois, boolean isDistribue) {
|
||||
this.questionText = questionText;
|
||||
this.isBois = isBois;
|
||||
this.isDistribue = isDistribue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traite les gorgées pour une question
|
||||
*/
|
||||
private GorgeeResult processGorgees(Question question, String questionText, int playerCount) {
|
||||
int totalGorgees = 0;
|
||||
boolean isBois = false;
|
||||
boolean isDistribue = false;
|
||||
|
||||
if (question.isDistribution() || question.isRecois()) {
|
||||
totalGorgees = question.getGorger() + ajoutGorgees;
|
||||
|
||||
ActionChoiceResult actionResult = determineActionChoice(question, questionText, playerCount);
|
||||
questionText = actionResult.questionText;
|
||||
isBois = actionResult.isBois;
|
||||
isDistribue = actionResult.isDistribue;
|
||||
|
||||
questionText = appendGorgeeCount(questionText, totalGorgees);
|
||||
}
|
||||
|
||||
return new GorgeeResult(questionText, totalGorgees, isBois, isDistribue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine l'action (boit/boivent ou distribue/distribuent) selon le nombre de joueurs
|
||||
*/
|
||||
private ActionChoiceResult determineActionChoice(Question question, String questionText, int playerCount) {
|
||||
boolean isBois = false;
|
||||
boolean isDistribue = false;
|
||||
|
||||
// Accord du verbe selon le nombre de joueurs
|
||||
String boisVerb = (playerCount > 1) ? "boivent" : "boit";
|
||||
String distribueVerb = (playerCount > 1) ? "distribuent" : "distribue";
|
||||
|
||||
if (question.isRecois() && question.isDistribution()) {
|
||||
boolean rand = random.nextBoolean();
|
||||
if (rand) {
|
||||
questionText = questionText + " <b>" + boisVerb + "</b>";
|
||||
isBois = true;
|
||||
} else {
|
||||
questionText = questionText + " <b>" + distribueVerb + "</b>";
|
||||
isDistribue = true;
|
||||
}
|
||||
} else if (question.isRecois()) {
|
||||
questionText = questionText + " <b>" + boisVerb + "</b>";
|
||||
isBois = true;
|
||||
} else if (question.isDistribution()) {
|
||||
questionText = questionText + " <b>" + distribueVerb + "</b>";
|
||||
isDistribue = true;
|
||||
}
|
||||
|
||||
return new ActionChoiceResult(questionText, isBois, isDistribue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute le nombre de gorgées au texte de la question
|
||||
*/
|
||||
private String appendGorgeeCount(String questionText, int totalGorgees) {
|
||||
String plural = totalGorgees > 1 ? "s" : "";
|
||||
return questionText + " " + totalGorgees + " gorgée" + plural + ".";
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour les statistiques du joueur J1
|
||||
*/
|
||||
private void updatePlayerStats(String j1Name, GorgeeResult gorgeeResult) {
|
||||
if (j1Name == null) return;
|
||||
|
||||
PlayerStats stats = playerStatsMap.get(j1Name);
|
||||
if (stats == null) {
|
||||
stats = new PlayerStats(j1Name);
|
||||
playerStatsMap.put(j1Name, stats);
|
||||
}
|
||||
|
||||
if (gorgeeResult.isBois) {
|
||||
stats.addGorgeesBuves(gorgeeResult.totalGorgees);
|
||||
}
|
||||
|
||||
if (gorgeeResult.isDistribue) {
|
||||
stats.addGorgeesDistribuees(gorgeeResult.totalGorgees);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sauvegarde les stats du jeu
|
||||
*/
|
||||
private void saveGameStats() {
|
||||
// TODO: Implémenter la sauvegarde des stats
|
||||
}
|
||||
|
||||
/**
|
||||
* Réinitialise les questions demandées
|
||||
*/
|
||||
private void resetAskedQuestions() {
|
||||
currentQuestionIndex = 0;
|
||||
totalQuestionsAsked = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sélectionne 3 joueurs aléatoires
|
||||
*/
|
||||
private ArrayList<String> TroisJoueurAleatoire() {
|
||||
ArrayList<String> selection = new ArrayList<>();
|
||||
if (toutlesjoueurs.size() <= 3) {
|
||||
return new ArrayList<>(toutlesjoueurs);
|
||||
}
|
||||
|
||||
while (selection.size() < 3) {
|
||||
String player = toutlesjoueurs.get(random.nextInt(toutlesjoueurs.size()));
|
||||
if (!selection.contains(player)) {
|
||||
selection.add(player);
|
||||
}
|
||||
}
|
||||
return selection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère le clic sur le bouton suivant
|
||||
*/
|
||||
public void OnClickButton1(View view) {
|
||||
updateQuestion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère le clic sur le bouton skip (Abandonner le défi)
|
||||
*/
|
||||
public void onSkipClick(View view) {
|
||||
// Si un défi est en cours, l'abandonner complètement
|
||||
if (!questionsAvecManches.isEmpty()) {
|
||||
abandonDefi();
|
||||
}
|
||||
|
||||
// Continuer normalement
|
||||
skipQuestion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Abandonne complètement le défi en cours
|
||||
*/
|
||||
private void abandonDefi() {
|
||||
// Supprimer le défi en cours
|
||||
questionsAvecManches.clear();
|
||||
|
||||
// Cacher le compteur de manches
|
||||
mancheCounterTextView.setVisibility(View.GONE);
|
||||
mancheQuestionText.setVisibility(View.GONE);
|
||||
|
||||
// Cacher le bouton skip
|
||||
updateSkipButtonVisibility();
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour la visibilité du bouton skip (affiché seulement pendant un défi)
|
||||
*/
|
||||
private void updateSkipButtonVisibility() {
|
||||
if (!questionsAvecManches.isEmpty()) {
|
||||
skipButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
skipButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Fermer la bulle si elle est ouverte
|
||||
hideDefiBubble();
|
||||
// if (soundManager != null) {
|
||||
// soundManager.release();
|
||||
// }
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.example.boidelov3.games.boideloclassic;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import com.example.boidelov3.R;
|
||||
|
||||
/**
|
||||
* BoideloClassicParamsActivity - Écran de paramètres pour Boidelo Classic
|
||||
*
|
||||
* Cette activité permet de configurer les paramètres du jeu :
|
||||
* - Nombre de questions
|
||||
* - Nombre de gorgées
|
||||
* - Activation/désactivation de l'IA
|
||||
* - Durée des défis
|
||||
*
|
||||
* C'est une version refactorisée de l'ancienne JeuxParametres.java
|
||||
*/
|
||||
public class BoideloClassicParamsActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_boidelo_classic_params);
|
||||
|
||||
// Configure la barre d'action avec un bouton retour
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setTitle(R.string.parameters);
|
||||
}
|
||||
|
||||
initViews();
|
||||
setupListeners();
|
||||
loadCurrentSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise les vues de l'activité
|
||||
*/
|
||||
private void initViews() {
|
||||
// TODO: Initialiser les vues pour les paramètres
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure les écouteurs d'événements
|
||||
*/
|
||||
private void setupListeners() {
|
||||
// TODO: Configurer les listeners pour les changements de paramètres
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge les paramètres actuels depuis les préférences
|
||||
*/
|
||||
private void loadCurrentSettings() {
|
||||
// TODO: Charger les paramètres depuis SharedPreferences
|
||||
}
|
||||
|
||||
/**
|
||||
* Sauvegarde les paramètres
|
||||
*/
|
||||
private void saveSettings() {
|
||||
// TODO: Sauvegarder les paramètres dans SharedPreferences
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSupportNavigateUp() {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
package com.example.boidelov3.games.boideloclassic;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.example.boidelov3.R;
|
||||
import com.google.android.material.appbar.MaterialToolbar;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BoideloClassicSetupActivity - Écran de configuration du jeu Boidelo Classic
|
||||
* Version simplifiée comme 89++
|
||||
*/
|
||||
public class BoideloClassicSetupActivity extends AppCompatActivity {
|
||||
|
||||
private static final int MIN_PLAYERS = 3;
|
||||
private static final int MAX_PLAYERS = 15;
|
||||
|
||||
private LinearLayout playersContainer;
|
||||
private MaterialButton addPlayerButton;
|
||||
private MaterialButton startGameButton;
|
||||
private TextView playerCountText;
|
||||
private MaterialToolbar toolbar;
|
||||
|
||||
private final List<String> playerNames = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_boidelo_classic_setup);
|
||||
|
||||
initViews();
|
||||
setupToolbar();
|
||||
setupListeners();
|
||||
|
||||
// Ajouter 3 joueurs par défaut
|
||||
addPlayerRow();
|
||||
addPlayerRow();
|
||||
addPlayerRow();
|
||||
|
||||
// Initialiser le compteur
|
||||
updatePlayerCountText();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
toolbar = findViewById(R.id.toolbar);
|
||||
playersContainer = findViewById(R.id.playersContainer);
|
||||
addPlayerButton = findViewById(R.id.addPlayerButton);
|
||||
startGameButton = findViewById(R.id.startGameButton);
|
||||
playerCountText = findViewById(R.id.playerCountText);
|
||||
}
|
||||
|
||||
private void setupToolbar() {
|
||||
toolbar.setNavigationOnClickListener(v -> finish());
|
||||
}
|
||||
|
||||
private void setupListeners() {
|
||||
addPlayerButton.setOnClickListener(v -> {
|
||||
if (playersContainer.getChildCount() < MAX_PLAYERS) {
|
||||
addPlayerRow();
|
||||
} else {
|
||||
Toast.makeText(this, "Maximum " + MAX_PLAYERS + " joueurs", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
startGameButton.setOnClickListener(v -> startGame());
|
||||
}
|
||||
|
||||
private void addPlayerRow() {
|
||||
View playerRow = LayoutInflater.from(this).inflate(R.layout.item_player_row, playersContainer, false);
|
||||
|
||||
TextInputEditText playerNameEdit = playerRow.findViewById(R.id.playerName);
|
||||
MaterialButton removeButton = playerRow.findViewById(R.id.removePlayerButton);
|
||||
TextView playerNumber = playerRow.findViewById(R.id.playerNumber);
|
||||
|
||||
int position = playersContainer.getChildCount();
|
||||
playerNumber.setText(String.valueOf(position + 1));
|
||||
|
||||
// Cacher le bouton de suppression pour les 3 premiers joueurs (minimum requis)
|
||||
if (position < MIN_PLAYERS) {
|
||||
removeButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
removeButton.setOnClickListener(v -> {
|
||||
if (playersContainer.getChildCount() > MIN_PLAYERS) {
|
||||
playersContainer.removeView(playerRow);
|
||||
updatePlayerNumbers();
|
||||
updatePlayerNames();
|
||||
} else {
|
||||
Toast.makeText(this, "Minimum " + MIN_PLAYERS + " joueurs", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
playerNameEdit.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (!hasFocus) {
|
||||
updatePlayerNames();
|
||||
}
|
||||
});
|
||||
|
||||
playersContainer.addView(playerRow);
|
||||
updatePlayerCountText();
|
||||
updateStartButton();
|
||||
}
|
||||
|
||||
private void updatePlayerNumbers() {
|
||||
for (int i = 0; i < playersContainer.getChildCount(); i++) {
|
||||
View row = playersContainer.getChildAt(i);
|
||||
TextView playerNumber = row.findViewById(R.id.playerNumber);
|
||||
playerNumber.setText(String.valueOf(i + 1));
|
||||
|
||||
// Afficher le bouton de suppression uniquement au-delà du minimum
|
||||
MaterialButton removeButton = row.findViewById(R.id.removePlayerButton);
|
||||
if (i >= MIN_PLAYERS) {
|
||||
removeButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
removeButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
updatePlayerCountText();
|
||||
}
|
||||
|
||||
private void updatePlayerNames() {
|
||||
playerNames.clear();
|
||||
for (int i = 0; i < playersContainer.getChildCount(); i++) {
|
||||
View row = playersContainer.getChildAt(i);
|
||||
TextInputEditText edit = row.findViewById(R.id.playerName);
|
||||
String name = edit.getText().toString().trim();
|
||||
if (!TextUtils.isEmpty(name)) {
|
||||
playerNames.add(name);
|
||||
} else {
|
||||
playerNames.add("Joueur " + (i + 1));
|
||||
}
|
||||
}
|
||||
updateStartButton();
|
||||
}
|
||||
|
||||
private void updatePlayerCountText() {
|
||||
int count = playersContainer.getChildCount();
|
||||
playerCountText.setText("Joueurs: " + count + " / min. " + MIN_PLAYERS);
|
||||
}
|
||||
|
||||
private void updateStartButton() {
|
||||
int validPlayers = playersContainer.getChildCount();
|
||||
boolean canStart = validPlayers >= MIN_PLAYERS;
|
||||
startGameButton.setEnabled(canStart);
|
||||
startGameButton.setText(canStart ? "JOUER (" + validPlayers + ")" : "Ajoutez des joueurs");
|
||||
}
|
||||
|
||||
private void startGame() {
|
||||
// Vérifier que tous les champs minimums sont remplis
|
||||
ArrayList<String> validNames = new ArrayList<>();
|
||||
for (int i = 0; i < playersContainer.getChildCount(); i++) {
|
||||
View row = playersContainer.getChildAt(i);
|
||||
TextInputEditText edit = row.findViewById(R.id.playerName);
|
||||
String name = edit.getText().toString().trim();
|
||||
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
Toast.makeText(this, "Veuillez remplir le nom du joueur " + (i + 1), Toast.LENGTH_SHORT).show();
|
||||
edit.requestFocus();
|
||||
return;
|
||||
}
|
||||
validNames.add(name);
|
||||
}
|
||||
|
||||
if (validNames.size() < MIN_PLAYERS) {
|
||||
Toast.makeText(this, "Minimum " + MIN_PLAYERS + " joueurs requis", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(this, BoideloClassicGameActivity.class);
|
||||
intent.putStringArrayListExtra("PLAYERS", validNames);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.example.boidelov3.games.boideloclassic.manager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* BoideloPlayerManager - Gestionnaire des joueurs pour Boidelo Classic
|
||||
* Cette classe gère la liste des joueurs et leurs informations
|
||||
*/
|
||||
public class BoideloPlayerManager {
|
||||
|
||||
private ArrayList<String> players;
|
||||
|
||||
public BoideloPlayerManager() {
|
||||
this.players = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute un joueur à la liste
|
||||
* @param playerName Le nom du joueur à ajouter
|
||||
*/
|
||||
public void addPlayer(String playerName) {
|
||||
if (playerName != null && !playerName.trim().isEmpty()) {
|
||||
players.add(playerName.trim());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un joueur de la liste
|
||||
* @param index L'index du joueur à supprimer
|
||||
*/
|
||||
public void removePlayer(int index) {
|
||||
if (index >= 0 && index < players.size()) {
|
||||
players.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la liste des joueurs
|
||||
* @return La liste des noms des joueurs
|
||||
*/
|
||||
public ArrayList<String> getPlayers() {
|
||||
return new ArrayList<>(players);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le nombre de joueurs
|
||||
* @return Le nombre de joueurs
|
||||
*/
|
||||
public int getPlayerCount() {
|
||||
return players.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vide la liste des joueurs
|
||||
*/
|
||||
public void clearPlayers() {
|
||||
players.clear();
|
||||
}
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
package com.example.boidelov3.games.boideloclassic.manager;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import com.example.boidelov3.R;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* BoideloPlayerRowManager - Gestionnaire des lignes de saisie de joueurs
|
||||
* Cette classe gère l'ajout et la suppression dynamique de champs de saisie
|
||||
* avec Material Design
|
||||
*/
|
||||
public class BoideloPlayerRowManager {
|
||||
|
||||
private Activity activity;
|
||||
private LinearLayout nameEntryLayout;
|
||||
private BoideloPlayerManager playerManager;
|
||||
private ArrayList<LinearLayout> playerRowList;
|
||||
private ArrayList<TextInputEditText> editTextList;
|
||||
|
||||
private int offset = 4; // Nombre initial de joueurs fixes (J1, J2, J3)
|
||||
|
||||
public BoideloPlayerRowManager(Activity activity, LinearLayout nameEntryLayout,
|
||||
BoideloPlayerManager playerManager) {
|
||||
this.activity = activity;
|
||||
this.nameEntryLayout = nameEntryLayout;
|
||||
this.playerManager = playerManager;
|
||||
this.playerRowList = new ArrayList<>();
|
||||
this.editTextList = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une nouvelle ligne de saisie pour un joueur
|
||||
*/
|
||||
public void addPlayerRow() {
|
||||
// Inflate le layout Material Design
|
||||
LayoutInflater inflater = LayoutInflater.from(activity);
|
||||
LinearLayout newRow = (LinearLayout) inflater.inflate(R.layout.item_player_row, nameEntryLayout, false);
|
||||
|
||||
// Configure le numéro du joueur
|
||||
TextView playerNumber = newRow.findViewById(R.id.playerNumber);
|
||||
playerNumber.setText(String.valueOf(offset));
|
||||
|
||||
// Récupère le champ de saisie
|
||||
TextInputEditText editText = newRow.findViewById(R.id.playerName);
|
||||
editText.setHint("Joueur " + offset);
|
||||
editTextList.add(editText);
|
||||
|
||||
// Configure le bouton de suppression
|
||||
MaterialButton removeButton = newRow.findViewById(R.id.removePlayerButton);
|
||||
removeButton.setOnClickListener(v -> removePlayerRow(newRow));
|
||||
|
||||
// Ajoute la ligne au conteneur
|
||||
nameEntryLayout.addView(newRow);
|
||||
playerRowList.add(newRow);
|
||||
offset++;
|
||||
|
||||
// Notifie le gestionnaire de joueurs
|
||||
playerManager.addPlayer("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une ligne de joueur
|
||||
* @param row La ligne à supprimer
|
||||
*/
|
||||
public void removePlayerRow(LinearLayout row) {
|
||||
int index = playerRowList.indexOf(row);
|
||||
if (index >= 0) {
|
||||
nameEntryLayout.removeView(row);
|
||||
playerRowList.remove(index);
|
||||
if (index < editTextList.size()) {
|
||||
editTextList.remove(index);
|
||||
}
|
||||
offset--;
|
||||
|
||||
// Met à jour les numéros de joueurs
|
||||
updatePlayerNumbers();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour les numéros affichés pour tous les joueurs
|
||||
*/
|
||||
private void updatePlayerNumbers() {
|
||||
for (int i = 0; i < playerRowList.size(); i++) {
|
||||
LinearLayout row = playerRowList.get(i);
|
||||
TextView playerNumber = row.findViewById(R.id.playerNumber);
|
||||
if (playerNumber != null) {
|
||||
playerNumber.setText(String.valueOf(4 + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne les noms des joueurs depuis les champs dynamiques
|
||||
* @return La liste des noms des joueurs
|
||||
*/
|
||||
public ArrayList<String> getPlayerNames() {
|
||||
ArrayList<String> names = new ArrayList<>();
|
||||
for (TextInputEditText editText : editTextList) {
|
||||
String name = editText.getText() != null ? editText.getText().toString().trim() : "";
|
||||
if (!name.isEmpty()) {
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le nombre total de lignes
|
||||
* @return Le nombre de lignes
|
||||
*/
|
||||
public int getRowCount() {
|
||||
return playerRowList.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.example.boidelov3.games.game89;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Gère les défis du jeu 89++
|
||||
*/
|
||||
public class Game89ChallengeManager {
|
||||
private final Random random;
|
||||
private final List<Challenge> availableChallenges;
|
||||
|
||||
public Game89ChallengeManager() {
|
||||
this.random = new Random();
|
||||
this.availableChallenges = new ArrayList<>();
|
||||
initializeChallenges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise la liste des défis disponibles
|
||||
*/
|
||||
private void initializeChallenges() {
|
||||
// Changer le sens
|
||||
availableChallenges.add(new Challenge(
|
||||
ChallengeType.REVERSE_DIRECTION,
|
||||
"Changement de sens !",
|
||||
"Le sens du jeu est inversé !"
|
||||
));
|
||||
|
||||
// Immunité
|
||||
availableChallenges.add(new Challenge(
|
||||
ChallengeType.IMMUNITY,
|
||||
"Immunité !",
|
||||
"Le joueur actuel est immunisé contre les gorgées pendant 5 minutes !"
|
||||
));
|
||||
|
||||
// Distribuer des gorgées
|
||||
availableChallenges.add(new Challenge(
|
||||
ChallengeType.DRINK_GORGEE,
|
||||
"Gorgée surprise !",
|
||||
"Le joueur actuel boit 3 gorgées !"
|
||||
));
|
||||
|
||||
// Joker : passer son tour
|
||||
availableChallenges.add(new Challenge(
|
||||
ChallengeType.SKIP_TURN,
|
||||
"Joker !",
|
||||
"Le joueur actuel passe son tour !"
|
||||
));
|
||||
|
||||
// Échange de main
|
||||
availableChallenges.add(new Challenge(
|
||||
ChallengeType.SWAP_HAND,
|
||||
"Échange de mains !",
|
||||
"Le joueur actuel échange sa main avec le joueur de son choix !"
|
||||
));
|
||||
|
||||
// Double prochaine valeur
|
||||
availableChallenges.add(new Challenge(
|
||||
ChallengeType.DOUBLE_NEXT,
|
||||
"Double !",
|
||||
"La prochaine carte jouée compte double !"
|
||||
));
|
||||
|
||||
// Compteur aléatoire
|
||||
availableChallenges.add(new Challenge(
|
||||
ChallengeType.RANDOM_COUNT,
|
||||
"Compte mystère !",
|
||||
"Ajoutez un nombre aléatoire entre 1 et 10 au compteur !"
|
||||
));
|
||||
|
||||
// Tout le monde boit
|
||||
availableChallenges.add(new Challenge(
|
||||
ChallengeType.EVERYONE_DRINKS,
|
||||
"Tour générale !",
|
||||
"Tout le monde boit 2 gorgées !"
|
||||
));
|
||||
|
||||
// Choisissez une victime
|
||||
availableChallenges.add(new Challenge(
|
||||
ChallengeType.PICK_VICTIM,
|
||||
"Victime !",
|
||||
"Le joueur actuel choisit quelqu'un qui boit 3 gorgées !"
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne un défi aléatoire
|
||||
*/
|
||||
public Challenge getRandomChallenge() {
|
||||
if (availableChallenges.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int index = random.nextInt(availableChallenges.size());
|
||||
return availableChallenges.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la description d'un défi pour l'afficher
|
||||
*/
|
||||
public static String getChallengeDisplay(Challenge challenge) {
|
||||
if (challenge == null) {
|
||||
return "";
|
||||
}
|
||||
return challenge.getTitle() + "\n" + challenge.getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* Classe représentant un défi
|
||||
*/
|
||||
public static class Challenge {
|
||||
private final ChallengeType type;
|
||||
private final String title;
|
||||
private final String description;
|
||||
|
||||
public Challenge(ChallengeType type, String title, String description) {
|
||||
this.type = type;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public ChallengeType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Types de défis disponibles
|
||||
*/
|
||||
public enum ChallengeType {
|
||||
REVERSE_DIRECTION,
|
||||
IMMUNITY,
|
||||
DRINK_GORGEE,
|
||||
SKIP_TURN,
|
||||
SWAP_HAND,
|
||||
DOUBLE_NEXT,
|
||||
RANDOM_COUNT,
|
||||
EVERYONE_DRINKS,
|
||||
PICK_VICTIM
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
package com.example.boidelov3.games.game89;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.GridLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.example.boidelov3.R;
|
||||
import com.google.android.material.appbar.MaterialToolbar;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Assistant pour le jeu 89++ - Affiche les défis et gère le timer
|
||||
* Les joueurs jouent avec de vraies cartes, l'application est juste un assistant
|
||||
*/
|
||||
public class Game89GameActivity extends AppCompatActivity {
|
||||
|
||||
// UI Components
|
||||
private MaterialToolbar toolbar;
|
||||
private TextView timerTextView;
|
||||
private TextView challengeTitleTextView;
|
||||
private TextView challengeDescriptionTextView;
|
||||
private View challengeContainer;
|
||||
private MaterialButton recordDrinksButton;
|
||||
private MaterialButton showStatsButton;
|
||||
|
||||
// Game Data
|
||||
private List<Game89Player> players;
|
||||
private Game89ChallengeManager challengeManager;
|
||||
private int challengeTimerMinutes;
|
||||
private CountDownTimer challengeTimer;
|
||||
private long timeUntilNextChallenge;
|
||||
private Game89ChallengeManager.Challenge currentChallenge;
|
||||
private boolean challengeActive = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_game89_game);
|
||||
|
||||
// Get intent data
|
||||
ArrayList<String> playerNames = getIntent().getStringArrayListExtra("PLAYERS");
|
||||
challengeTimerMinutes = getIntent().getIntExtra("CHALLENGE_TIMER_MINUTES", 1);
|
||||
|
||||
if (playerNames == null || playerNames.isEmpty()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
initViews();
|
||||
setupToolbar();
|
||||
setupGame(playerNames);
|
||||
setupListeners();
|
||||
startChallengeTimer();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
toolbar = findViewById(R.id.toolbar);
|
||||
timerTextView = findViewById(R.id.timerTextView);
|
||||
challengeTitleTextView = findViewById(R.id.challengeTitleTextView);
|
||||
challengeDescriptionTextView = findViewById(R.id.challengeDescriptionTextView);
|
||||
challengeContainer = findViewById(R.id.challengeContainer);
|
||||
recordDrinksButton = findViewById(R.id.recordDrinksButton);
|
||||
showStatsButton = findViewById(R.id.showStatsButton);
|
||||
}
|
||||
|
||||
private void setupToolbar() {
|
||||
toolbar.setNavigationOnClickListener(v -> finish());
|
||||
}
|
||||
|
||||
private void setupGame(ArrayList<String> playerNames) {
|
||||
players = new ArrayList<>();
|
||||
for (String name : playerNames) {
|
||||
players.add(new Game89Player(name));
|
||||
}
|
||||
|
||||
challengeManager = new Game89ChallengeManager();
|
||||
|
||||
// Show initial state
|
||||
challengeContainer.setVisibility(View.GONE);
|
||||
timerTextView.setText("En attente du premier défi...");
|
||||
}
|
||||
|
||||
private void setupListeners() {
|
||||
recordDrinksButton.setOnClickListener(v -> showRecordDrinksDialog());
|
||||
showStatsButton.setOnClickListener(v -> showStatsDialog());
|
||||
|
||||
// Click on challenge to dismiss it and resume timer
|
||||
challengeContainer.setOnClickListener(v -> dismissChallenge());
|
||||
}
|
||||
|
||||
private void startChallengeTimer() {
|
||||
// Ne pas démarrer si un défi est actif
|
||||
if (challengeActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
timeUntilNextChallenge = challengeTimerMinutes * 60 * 1000L;
|
||||
|
||||
challengeTimer = new CountDownTimer(timeUntilNextChallenge, 1000) {
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
// Arrêter le tick si un défi est devenu actif
|
||||
if (challengeActive) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
timeUntilNextChallenge = millisUntilFinished;
|
||||
long minutes = millisUntilFinished / 60000;
|
||||
long seconds = (millisUntilFinished % 60000) / 1000;
|
||||
timerTextView.setText(String.format(Locale.getDefault(),
|
||||
"Prochain défi dans: %d:%02d", minutes, seconds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
// Ne déclencher que si aucun défi n'est actif
|
||||
if (!challengeActive) {
|
||||
triggerChallenge();
|
||||
startChallengeTimer(); // Restart timer for next challenge
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
private void triggerChallenge() {
|
||||
currentChallenge = challengeManager.getRandomChallenge();
|
||||
if (currentChallenge == null) return;
|
||||
|
||||
challengeActive = true;
|
||||
|
||||
// Pause the timer
|
||||
if (challengeTimer != null) {
|
||||
challengeTimer.cancel();
|
||||
}
|
||||
|
||||
// Update UI - Hide timer text when challenge is active
|
||||
timerTextView.setVisibility(View.GONE);
|
||||
challengeContainer.setVisibility(View.VISIBLE);
|
||||
challengeTitleTextView.setText(currentChallenge.getTitle());
|
||||
challengeDescriptionTextView.setText(currentChallenge.getDescription());
|
||||
|
||||
// Vibrate to alert players
|
||||
try {
|
||||
android.os.Vibrator vibrator = (android.os.Vibrator) getSystemService(android.content.Context.VIBRATOR_SERVICE);
|
||||
if (vibrator != null) {
|
||||
vibrator.vibrate(new long[]{0, 300, 200, 300}, -1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore vibration errors
|
||||
}
|
||||
}
|
||||
|
||||
private void dismissChallenge() {
|
||||
challengeContainer.setVisibility(View.GONE);
|
||||
challengeActive = false;
|
||||
timerTextView.setVisibility(View.VISIBLE);
|
||||
|
||||
// Resume the timer
|
||||
startChallengeTimer();
|
||||
}
|
||||
|
||||
private void showRecordDrinksDialog() {
|
||||
if (players.isEmpty()) return;
|
||||
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
|
||||
builder.setTitle("Enregistrer des gorgées");
|
||||
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setOrientation(LinearLayout.VERTICAL);
|
||||
layout.setPadding(50, 40, 50, 10);
|
||||
|
||||
// Create player selection
|
||||
String[] playerNames = new String[players.size()];
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
playerNames[i] = players.get(i).getName();
|
||||
}
|
||||
|
||||
final int[] selectedPlayer = {0};
|
||||
|
||||
TextView label = new TextView(this);
|
||||
label.setText("Sélectionnez le joueur:");
|
||||
label.setPadding(0, 0, 0, 20);
|
||||
layout.addView(label);
|
||||
|
||||
// Create a grid of buttons for each player
|
||||
GridLayout playerGrid = new GridLayout(this);
|
||||
playerGrid.setColumnCount(2);
|
||||
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
final int playerIndex = i;
|
||||
MaterialButton playerButton = new MaterialButton(this);
|
||||
playerButton.setText(players.get(i).getName());
|
||||
playerButton.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
GridLayout.LayoutParams params = new GridLayout.LayoutParams();
|
||||
params.setMargins(8, 8, 8, 8);
|
||||
playerButton.setLayoutParams(params);
|
||||
|
||||
playerButton.setOnClickListener(v -> {
|
||||
selectedPlayer[0] = playerIndex;
|
||||
showGorgeesCountDialog(playerIndex);
|
||||
});
|
||||
|
||||
playerGrid.addView(playerButton);
|
||||
}
|
||||
|
||||
layout.addView(playerGrid);
|
||||
|
||||
builder.setView(layout);
|
||||
builder.setNegativeButton("Fermer", null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void showGorgeesCountDialog(int playerIndex) {
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
|
||||
builder.setTitle("Gorgées pour " + players.get(playerIndex).getName());
|
||||
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setOrientation(LinearLayout.VERTICAL);
|
||||
layout.setPadding(60, 50, 60, 20);
|
||||
|
||||
// Label pour afficher la valeur actuelle
|
||||
TextView valueLabel = new TextView(this);
|
||||
valueLabel.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
|
||||
valueLabel.setTextSize(24);
|
||||
valueLabel.setText("1 gorgée");
|
||||
valueLabel.setPadding(0, 0, 0, 20);
|
||||
layout.addView(valueLabel);
|
||||
|
||||
// SeekBar de 1 à 8
|
||||
SeekBar seekBar = new SeekBar(this);
|
||||
seekBar.setMax(7); // 0-7, donc 1-8 avec le +1
|
||||
seekBar.setProgress(0); // Commence à 1 (0 + 1)
|
||||
seekBar.setPadding(0, 0, 0, 20);
|
||||
|
||||
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
int value = progress + 1; // 1-8
|
||||
valueLabel.setText(value + " gorgée" + (value > 1 ? "s" : ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {}
|
||||
});
|
||||
|
||||
layout.addView(seekBar);
|
||||
|
||||
builder.setView(layout);
|
||||
|
||||
builder.setPositiveButton("Valider", (dialog, which) -> {
|
||||
int count = seekBar.getProgress() + 1; // 1-8
|
||||
players.get(playerIndex).addGorgees(count);
|
||||
Toast.makeText(this,
|
||||
players.get(playerIndex).getName() + " boit " + count + " gorgée(s)!",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
builder.setNegativeButton("Annuler", null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void showStatsDialog() {
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
|
||||
builder.setTitle("Statistiques des gorgées");
|
||||
|
||||
StringBuilder stats = new StringBuilder();
|
||||
for (Game89Player player : players) {
|
||||
stats.append(player.getName())
|
||||
.append(": ")
|
||||
.append(player.getTotalGorgees())
|
||||
.append(" gorgée(s)\n\n");
|
||||
}
|
||||
|
||||
builder.setMessage(stats.toString());
|
||||
builder.setPositiveButton("Fermer", null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (challengeTimer != null) {
|
||||
challengeTimer.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.example.boidelov3.games.game89;
|
||||
|
||||
/**
|
||||
* Représente un joueur du jeu 89++ (version simplifiée pour assistant)
|
||||
*/
|
||||
public class Game89Player {
|
||||
private final String name;
|
||||
private int totalGorgees;
|
||||
|
||||
public Game89Player(String name) {
|
||||
this.name = name;
|
||||
this.totalGorgees = 0;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute des gorgées aux statistiques du joueur
|
||||
*/
|
||||
public void addGorgees(int count) {
|
||||
this.totalGorgees += count;
|
||||
}
|
||||
|
||||
public int getTotalGorgees() {
|
||||
return totalGorgees;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + " (" + totalGorgees + " gorgées)";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package com.example.boidelov3.games.game89;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.example.boidelov3.R;
|
||||
import com.google.android.material.appbar.MaterialToolbar;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Activity de configuration pour le jeu 89++
|
||||
*/
|
||||
public class Game89SetupActivity extends AppCompatActivity {
|
||||
|
||||
private static final int MIN_PLAYERS = 2;
|
||||
private static final int MAX_PLAYERS = 10;
|
||||
|
||||
private LinearLayout playersContainer;
|
||||
private MaterialButton addPlayerButton;
|
||||
private MaterialButton startGameButton;
|
||||
private SeekBar timerSeekBar;
|
||||
private TextView timerText;
|
||||
private MaterialToolbar toolbar;
|
||||
|
||||
private final List<String> playerNames = new ArrayList<>();
|
||||
private int challengeTimerMinutes = 1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_game89_setup);
|
||||
|
||||
initViews();
|
||||
setupToolbar();
|
||||
setupListeners();
|
||||
|
||||
// Ajouter 2 joueurs par défaut
|
||||
addPlayerRow();
|
||||
addPlayerRow();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
toolbar = findViewById(R.id.toolbar);
|
||||
playersContainer = findViewById(R.id.playersContainer);
|
||||
addPlayerButton = findViewById(R.id.addPlayerButton);
|
||||
startGameButton = findViewById(R.id.startGameButton);
|
||||
timerSeekBar = findViewById(R.id.timerSeekBar);
|
||||
timerText = findViewById(R.id.timerText);
|
||||
}
|
||||
|
||||
private void setupToolbar() {
|
||||
toolbar.setNavigationOnClickListener(v -> finish());
|
||||
}
|
||||
|
||||
private void setupListeners() {
|
||||
addPlayerButton.setOnClickListener(v -> {
|
||||
if (playerNames.size() < MAX_PLAYERS) {
|
||||
addPlayerRow();
|
||||
} else {
|
||||
Toast.makeText(this, "Maximum " + MAX_PLAYERS + " joueurs", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
startGameButton.setOnClickListener(v -> startGame());
|
||||
|
||||
timerSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
challengeTimerMinutes = progress;
|
||||
updateTimerText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {}
|
||||
});
|
||||
}
|
||||
|
||||
private void addPlayerRow() {
|
||||
View playerRow = LayoutInflater.from(this).inflate(R.layout.item_player_row, playersContainer, false);
|
||||
|
||||
TextInputEditText playerNameEdit = playerRow.findViewById(R.id.playerName);
|
||||
MaterialButton removeButton = playerRow.findViewById(R.id.removePlayerButton);
|
||||
TextView playerNumber = playerRow.findViewById(R.id.playerNumber);
|
||||
|
||||
int position = playersContainer.getChildCount();
|
||||
playerNumber.setText(String.valueOf(position + 1));
|
||||
|
||||
// Cacher le bouton de suppression pour les 2 premiers joueurs (minimum requis)
|
||||
if (position < MIN_PLAYERS) {
|
||||
removeButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
removeButton.setOnClickListener(v -> {
|
||||
if (playersContainer.getChildCount() > MIN_PLAYERS) {
|
||||
playersContainer.removeView(playerRow);
|
||||
updatePlayerNumbers();
|
||||
updatePlayerNames();
|
||||
} else {
|
||||
Toast.makeText(this, "Minimum " + MIN_PLAYERS + " joueurs", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
playerNameEdit.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (!hasFocus) {
|
||||
updatePlayerNames();
|
||||
}
|
||||
});
|
||||
|
||||
playersContainer.addView(playerRow);
|
||||
}
|
||||
|
||||
private void updatePlayerNumbers() {
|
||||
for (int i = 0; i < playersContainer.getChildCount(); i++) {
|
||||
View row = playersContainer.getChildAt(i);
|
||||
TextView playerNumber = row.findViewById(R.id.playerNumber);
|
||||
playerNumber.setText(String.valueOf(i + 1));
|
||||
|
||||
// Afficher le bouton de suppression uniquement au-delà du minimum
|
||||
MaterialButton removeButton = row.findViewById(R.id.removePlayerButton);
|
||||
if (i >= MIN_PLAYERS) {
|
||||
removeButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
removeButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePlayerNames() {
|
||||
playerNames.clear();
|
||||
for (int i = 0; i < playersContainer.getChildCount(); i++) {
|
||||
View row = playersContainer.getChildAt(i);
|
||||
TextInputEditText edit = row.findViewById(R.id.playerName);
|
||||
String name = edit.getText().toString().trim();
|
||||
if (!TextUtils.isEmpty(name)) {
|
||||
playerNames.add(name);
|
||||
} else {
|
||||
playerNames.add("Joueur " + (i + 1));
|
||||
}
|
||||
}
|
||||
updateStartButton();
|
||||
}
|
||||
|
||||
private void updateStartButton() {
|
||||
int validPlayers = playerNames.size();
|
||||
boolean canStart = validPlayers >= MIN_PLAYERS;
|
||||
startGameButton.setEnabled(canStart);
|
||||
startGameButton.setText(canStart ? "JOUER (" + validPlayers + ")" : "Ajoutez des joueurs");
|
||||
}
|
||||
|
||||
private void updateTimerText() {
|
||||
timerText.setText(challengeTimerMinutes + " minute" + (challengeTimerMinutes > 1 ? "s" : ""));
|
||||
}
|
||||
|
||||
private void startGame() {
|
||||
updatePlayerNames();
|
||||
|
||||
if (playerNames.size() < MIN_PLAYERS) {
|
||||
Toast.makeText(this, "Minimum " + MIN_PLAYERS + " joueurs requis", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(this, Game89GameActivity.class);
|
||||
intent.putStringArrayListExtra("PLAYERS", new ArrayList<>(playerNames));
|
||||
intent.putExtra("CHALLENGE_TIMER_MINUTES", challengeTimerMinutes);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.example.boidelov3.hub;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.boidelov3.R;
|
||||
import com.example.boidelov3.games.boideloclassic.BoideloClassicSetupActivity;
|
||||
import com.example.boidelov3.hub.adapter.GameAdapter;
|
||||
import com.example.boidelov3.hub.model.GameInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* GameSelectionActivity - Hub principal de sélection des jeux
|
||||
* Cette activité remplace l'ancienne MainActivity et sert de point d'entrée
|
||||
* pour tous les jeux disponibles dans Boidelo.
|
||||
*/
|
||||
public class GameSelectionActivity extends AppCompatActivity implements GameAdapter.OnItemClickListener {
|
||||
|
||||
private RecyclerView gamesRecyclerView;
|
||||
private GameAdapter gameAdapter;
|
||||
private List<GameInfo> gamesList;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_game_selection);
|
||||
|
||||
initViews();
|
||||
setupGamesList();
|
||||
setupRecyclerView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise les vues de l'activité
|
||||
*/
|
||||
private void initViews() {
|
||||
gamesRecyclerView = findViewById(R.id.gamesRecyclerView);
|
||||
gamesRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure la liste des jeux disponibles
|
||||
*/
|
||||
private void setupGamesList() {
|
||||
gamesList = new ArrayList<>();
|
||||
|
||||
// Boidelo Classic - Le jeu original
|
||||
gamesList.add(new GameInfo(
|
||||
"Boidelo Classic",
|
||||
"Le jeu original de questions et défis",
|
||||
R.drawable.ic_boidelo_classic,
|
||||
GameInfo.GameType.BOIDELO_CLASSIC,
|
||||
true
|
||||
));
|
||||
|
||||
// 89++ - Jeu de cartes
|
||||
gamesList.add(new GameInfo(
|
||||
"89++",
|
||||
"Jeu de cartes avec timer et défis",
|
||||
R.drawable.ic_game_89,
|
||||
GameInfo.GameType.GAME_89,
|
||||
true // Available now
|
||||
));
|
||||
|
||||
// Undercover - Jeu de déduction
|
||||
gamesList.add(new GameInfo(
|
||||
"Undercover",
|
||||
"Trouvez l'undercover avant qu'il ne soit trop tard!",
|
||||
R.drawable.ic_undercover,
|
||||
GameInfo.GameType.UNDERCOVER,
|
||||
false // Coming soon
|
||||
));
|
||||
|
||||
// Jeux de règles
|
||||
gamesList.add(new GameInfo(
|
||||
"Règles de jeux",
|
||||
"Découvrez les règles des jeux d'ambiance populaires",
|
||||
R.drawable.ic_rules,
|
||||
GameInfo.GameType.RULES,
|
||||
false // Coming soon
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure le RecyclerView avec l'adaptateur
|
||||
*/
|
||||
private void setupRecyclerView() {
|
||||
gameAdapter = new GameAdapter(gamesList, this);
|
||||
gamesRecyclerView.setAdapter(gameAdapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère le clic sur un jeu de la liste
|
||||
* @param gameInfo Le jeu sélectionné
|
||||
*/
|
||||
@Override
|
||||
public void onItemClick(GameInfo gameInfo) {
|
||||
if (!gameInfo.isAvailable()) {
|
||||
// Afficher un message "Coming soon"
|
||||
return;
|
||||
}
|
||||
|
||||
switch (gameInfo.getGameType()) {
|
||||
case BOIDELO_CLASSIC:
|
||||
startActivity(new Intent(this, BoideloClassicSetupActivity.class));
|
||||
break;
|
||||
case GAME_89:
|
||||
startActivity(new Intent(this, com.example.boidelov3.games.game89.Game89SetupActivity.class));
|
||||
break;
|
||||
case UNDERCOVER:
|
||||
// TODO: Implémenter UndercoverSetupActivity
|
||||
break;
|
||||
case RULES:
|
||||
// TODO: Implémenter RulesListActivity
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.example.boidelov3.hub.adapter;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.boidelov3.R;
|
||||
import com.example.boidelov3.hub.model.GameInfo;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* GameAdapter - Adaptateur pour afficher la liste des jeux dans le RecyclerView
|
||||
*/
|
||||
public class GameAdapter extends RecyclerView.Adapter<GameAdapter.GameViewHolder> {
|
||||
|
||||
private List<GameInfo> gamesList;
|
||||
private OnItemClickListener listener;
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(GameInfo gameInfo);
|
||||
}
|
||||
|
||||
public GameAdapter(List<GameInfo> gamesList, OnItemClickListener listener) {
|
||||
this.gamesList = gamesList;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GameViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_game_card, parent, false);
|
||||
return new GameViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull GameViewHolder holder, int position) {
|
||||
GameInfo gameInfo = gamesList.get(position);
|
||||
holder.bind(gameInfo, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return gamesList.size();
|
||||
}
|
||||
|
||||
static class GameViewHolder extends RecyclerView.ViewHolder {
|
||||
private ImageView gameIcon;
|
||||
private TextView gameName;
|
||||
private TextView gameDescription;
|
||||
private View statusText;
|
||||
|
||||
public GameViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
gameIcon = itemView.findViewById(R.id.gameIcon);
|
||||
gameName = itemView.findViewById(R.id.gameName);
|
||||
gameDescription = itemView.findViewById(R.id.gameDescription);
|
||||
statusText = itemView.findViewById(R.id.statusText);
|
||||
}
|
||||
|
||||
public void bind(GameInfo gameInfo, OnItemClickListener listener) {
|
||||
gameName.setText(gameInfo.getName());
|
||||
gameDescription.setText(gameInfo.getDescription());
|
||||
gameIcon.setImageResource(gameInfo.getIconResId());
|
||||
|
||||
if (gameInfo.isAvailable()) {
|
||||
statusText.setVisibility(View.GONE);
|
||||
itemView.setEnabled(true);
|
||||
itemView.setAlpha(1.0f);
|
||||
itemView.setOnClickListener(v -> listener.onItemClick(gameInfo));
|
||||
} else {
|
||||
statusText.setVisibility(View.VISIBLE);
|
||||
itemView.setEnabled(false);
|
||||
itemView.setAlpha(0.6f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.example.boidelov3.hub.model;
|
||||
|
||||
/**
|
||||
* GameInfo - Modèle représentant un jeu dans le hub
|
||||
* Contient les informations de base pour afficher et lancer un jeu
|
||||
*/
|
||||
public class GameInfo {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private int iconResId;
|
||||
private GameType gameType;
|
||||
private boolean available;
|
||||
|
||||
public enum GameType {
|
||||
BOIDELO_CLASSIC,
|
||||
GAME_89,
|
||||
UNDERCOVER,
|
||||
RULES
|
||||
}
|
||||
|
||||
public GameInfo(String name, String description, int iconResId, GameType gameType, boolean available) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.iconResId = iconResId;
|
||||
this.gameType = gameType;
|
||||
this.available = available;
|
||||
}
|
||||
|
||||
// Getters
|
||||
public String getName() { return name; }
|
||||
public String getDescription() { return description; }
|
||||
public int getIconResId() { return iconResId; }
|
||||
public GameType getGameType() { return gameType; }
|
||||
public boolean isAvailable() { return available; }
|
||||
|
||||
// Setters
|
||||
public void setAvailable(boolean available) { this.available = available; }
|
||||
}
|
||||
Reference in New Issue
Block a user