diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 792ee66..dee9a34 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,15 +15,48 @@ android:theme="@style/Theme.BoideloV3" tools:targetApi="31"> - + + + + + + + + + + + + + + + $1 + questions; + private ArrayList toutlesjoueurs; + private List questionsAvecManches; + private Map 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 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 = "🏆 Fin du défi !

" + 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 + if (hasActiveDefi && question.getQuestion().contains("")) { + 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("", chosenVariante); + } + return questionText; + } + + /** + * Gère les manches pour les défis + */ + private String processManches(Question question, String questionText) { + if (questionText.contains("")) { + int nbaleatoiremanches = calculateManchesCount(); + questionText = questionText.replace("", 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(""); + boolean isJoueurs2 = questionText.contains(""); + boolean isJoueurs3 = questionText.contains(""); + + String j1Name = null; + int playerCount = 0; + + if (isJoueurs1 || isJoueurs2 || isJoueurs3) { + List aleatoirejoueurs = TroisJoueurAleatoire(); + + if (isJoueurs1 && isJoueurs2 && isJoueurs3 && aleatoirejoueurs.size() >= RANDOM_PLAYER_SELECTION_COUNT) { + playerCount = 3; + j1Name = aleatoirejoueurs.get(0); + questionText = questionText.replace("", ErrorHandler.escapeHtml(j1Name)); + questionText = questionText.replace("", ErrorHandler.escapeHtml(aleatoirejoueurs.get(1))); + questionText = questionText.replace("", ErrorHandler.escapeHtml(aleatoirejoueurs.get(2))); + } else if (isJoueurs1 && isJoueurs2 && aleatoirejoueurs.size() >= TWO_PLAYERS) { + playerCount = 2; + j1Name = aleatoirejoueurs.get(0); + questionText = questionText.replace("", ErrorHandler.escapeHtml(j1Name)); + questionText = questionText.replace("", ErrorHandler.escapeHtml(aleatoirejoueurs.get(1))); + } else if (isJoueurs1 && aleatoirejoueurs.size() >= ONE_PLAYER) { + playerCount = 1; + j1Name = aleatoirejoueurs.get(0); + questionText = questionText.replace("", 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 + " " + boisVerb + ""; + isBois = true; + } else { + questionText = questionText + " " + distribueVerb + ""; + isDistribue = true; + } + } else if (question.isRecois()) { + questionText = questionText + " " + boisVerb + ""; + isBois = true; + } else if (question.isDistribution()) { + questionText = questionText + " " + distribueVerb + ""; + 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 TroisJoueurAleatoire() { + ArrayList 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(); + // } + } +} diff --git a/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicParamsActivity.java b/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicParamsActivity.java new file mode 100644 index 0000000..5278618 --- /dev/null +++ b/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicParamsActivity.java @@ -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; + } +} diff --git a/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicSetupActivity.java b/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicSetupActivity.java new file mode 100644 index 0000000..18870fa --- /dev/null +++ b/app/src/main/java/com/example/boidelov3/games/boideloclassic/BoideloClassicSetupActivity.java @@ -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 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 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); + } +} diff --git a/app/src/main/java/com/example/boidelov3/games/boideloclassic/manager/BoideloPlayerManager.java b/app/src/main/java/com/example/boidelov3/games/boideloclassic/manager/BoideloPlayerManager.java new file mode 100644 index 0000000..743cee4 --- /dev/null +++ b/app/src/main/java/com/example/boidelov3/games/boideloclassic/manager/BoideloPlayerManager.java @@ -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 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 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(); + } +} diff --git a/app/src/main/java/com/example/boidelov3/games/boideloclassic/manager/BoideloPlayerRowManager.java b/app/src/main/java/com/example/boidelov3/games/boideloclassic/manager/BoideloPlayerRowManager.java new file mode 100644 index 0000000..421e8e8 --- /dev/null +++ b/app/src/main/java/com/example/boidelov3/games/boideloclassic/manager/BoideloPlayerRowManager.java @@ -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 playerRowList; + private ArrayList 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 getPlayerNames() { + ArrayList 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(); + } +} diff --git a/app/src/main/java/com/example/boidelov3/games/game89/Game89ChallengeManager.java b/app/src/main/java/com/example/boidelov3/games/game89/Game89ChallengeManager.java new file mode 100644 index 0000000..5f56618 --- /dev/null +++ b/app/src/main/java/com/example/boidelov3/games/game89/Game89ChallengeManager.java @@ -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 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 + } +} diff --git a/app/src/main/java/com/example/boidelov3/games/game89/Game89GameActivity.java b/app/src/main/java/com/example/boidelov3/games/game89/Game89GameActivity.java new file mode 100644 index 0000000..a18fa31 --- /dev/null +++ b/app/src/main/java/com/example/boidelov3/games/game89/Game89GameActivity.java @@ -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 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 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 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(); + } + } +} diff --git a/app/src/main/java/com/example/boidelov3/games/game89/Game89Player.java b/app/src/main/java/com/example/boidelov3/games/game89/Game89Player.java new file mode 100644 index 0000000..324c5f6 --- /dev/null +++ b/app/src/main/java/com/example/boidelov3/games/game89/Game89Player.java @@ -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)"; + } +} diff --git a/app/src/main/java/com/example/boidelov3/games/game89/Game89SetupActivity.java b/app/src/main/java/com/example/boidelov3/games/game89/Game89SetupActivity.java new file mode 100644 index 0000000..d750162 --- /dev/null +++ b/app/src/main/java/com/example/boidelov3/games/game89/Game89SetupActivity.java @@ -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 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); + } +} diff --git a/app/src/main/java/com/example/boidelov3/hub/GameSelectionActivity.java b/app/src/main/java/com/example/boidelov3/hub/GameSelectionActivity.java new file mode 100644 index 0000000..c1c75db --- /dev/null +++ b/app/src/main/java/com/example/boidelov3/hub/GameSelectionActivity.java @@ -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 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; + } + } +} diff --git a/app/src/main/java/com/example/boidelov3/hub/adapter/GameAdapter.java b/app/src/main/java/com/example/boidelov3/hub/adapter/GameAdapter.java new file mode 100644 index 0000000..3649e4c --- /dev/null +++ b/app/src/main/java/com/example/boidelov3/hub/adapter/GameAdapter.java @@ -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 { + + private List gamesList; + private OnItemClickListener listener; + + public interface OnItemClickListener { + void onItemClick(GameInfo gameInfo); + } + + public GameAdapter(List 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); + } + } + } +} diff --git a/app/src/main/java/com/example/boidelov3/hub/model/GameInfo.java b/app/src/main/java/com/example/boidelov3/hub/model/GameInfo.java new file mode 100644 index 0000000..aed1b4a --- /dev/null +++ b/app/src/main/java/com/example/boidelov3/hub/model/GameInfo.java @@ -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; } +} diff --git a/app/src/main/res/drawable/bg_card.xml b/app/src/main/res/drawable/bg_card.xml index fd9bf78..dfe76c5 100644 --- a/app/src/main/res/drawable/bg_card.xml +++ b/app/src/main/res/drawable/bg_card.xml @@ -1,6 +1,6 @@ - + + + + diff --git a/app/src/main/res/drawable/ic_game_89.xml b/app/src/main/res/drawable/ic_game_89.xml new file mode 100644 index 0000000..ebce69b --- /dev/null +++ b/app/src/main/res/drawable/ic_game_89.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_rules.xml b/app/src/main/res/drawable/ic_rules.xml new file mode 100644 index 0000000..a1b9a0f --- /dev/null +++ b/app/src/main/res/drawable/ic_rules.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_undercover.xml b/app/src/main/res/drawable/ic_undercover.xml new file mode 100644 index 0000000..6858717 --- /dev/null +++ b/app/src/main/res/drawable/ic_undercover.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/layout/activity_boidelo_classic_game.xml b/app/src/main/res/layout/activity_boidelo_classic_game.xml new file mode 100644 index 0000000..110715a --- /dev/null +++ b/app/src/main/res/layout/activity_boidelo_classic_game.xml @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_boidelo_classic_params.xml b/app/src/main/res/layout/activity_boidelo_classic_params.xml new file mode 100644 index 0000000..c536cbd --- /dev/null +++ b/app/src/main/res/layout/activity_boidelo_classic_params.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_boidelo_classic_setup.xml b/app/src/main/res/layout/activity_boidelo_classic_setup.xml new file mode 100644 index 0000000..f1d4877 --- /dev/null +++ b/app/src/main/res/layout/activity_boidelo_classic_setup.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_game89_game.xml b/app/src/main/res/layout/activity_game89_game.xml new file mode 100644 index 0000000..f241dbc --- /dev/null +++ b/app/src/main/res/layout/activity_game89_game.xml @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_game89_setup.xml b/app/src/main/res/layout/activity_game89_setup.xml new file mode 100644 index 0000000..5446c04 --- /dev/null +++ b/app/src/main/res/layout/activity_game89_setup.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_game_selection.xml b/app/src/main/res/layout/activity_game_selection.xml new file mode 100644 index 0000000..c273159 --- /dev/null +++ b/app/src/main/res/layout/activity_game_selection.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_game_card.xml b/app/src/main/res/layout/item_game_card.xml new file mode 100644 index 0000000..7ca1fae --- /dev/null +++ b/app/src/main/res/layout/item_game_card.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_player_row.xml b/app/src/main/res/layout/item_player_row.xml new file mode 100644 index 0000000..64a3e38 --- /dev/null +++ b/app/src/main/res/layout/item_player_row.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 33456d8..819bcb1 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -1,50 +1,52 @@ - + - - #1E1E1E - #2D2D2D - #3A3A3A - #2D2D2D + + #000807 + #0D0D1A + #1A1A2E + #141428 - - #FFFFFF - #E0E0E0 - #9E9E9E + + #FBF9FF + #B3B7EE + #9395D3 + #000807 - - #B388FF - #7C4DFF - #D1C4E9 + + #B3B7EE + #9395D3 + #B3B7EE - - #2D2D2D - #1E1E1E - #2D2D2D - #372E45 - #413E5A - #4B4E6F - #2E3E5A - #5A4E2E - #5A3E2E + + #0D0D1A + #000807 + #141428 + #1E1E35 + #2A2A45 + #373755 + #2A2A45 + #3A3520 + #3A2520 #81C784 #EF5350 #FFB74D - - #FFFFFF - #000000 - #B388FF - #E0E0E0 - #4A3A69 - #EDE7F6 - #404040 - #404040 - #FFFFFF - #FFFFFF + + #2A2A45 + #FBF9FF + #2A2A45 + #2A2A45 + + #80000000 #80FFFFFF + + + #FBF9FF + #000807 + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 4336f1b..004d9ee 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,50 +1,51 @@ - - - - #FFFFFF - #FFFFFF - #F0F0F0 + + + + #9395D3 + #000807 + #B3B7EE + + + #FBF9FF + #FBF9FF + #FBF9FF #FFFFFF - - - #000000 - #333333 - #999999 - - - #7C4DFF - #6200EA - #B388FF - - - #FFFFFF - #FFFFFF + + + #000807 + #A2A3BB + #9395D3 + #FBF9FF + + + #FBF9FF + #FBF9FF #FFFFFF - #F3E5F5 - #E1BEE7 - #CE93D8 - #E8EAF6 + #E8EAF6 + #D1C4E9 + #B3B7EE + #C5CAE9 #FFF59D #FFCCBC - + #4CAF50 #F44336 #FF9800 - - - #FFFFFF - #000000 - #7C4DFF - #333333 - #EDE7F6 - #1A0033 + + + #E8EAF6 + #000807 #E0E0E0 - #E0E0E0 - #000000 - #FFFFFF + #B3B7EE + + #80000000 #80FFFFFF + + + #FFFFFF + #000807 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a5048e7..37734e7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,5 +20,61 @@ Activer les questions par IA Clé API Intelligence Artificielle + + Bienvenue sur Boidelo Hub + Choisissez un jeu pour commencer + Icône du jeu + Jouer + Bientôt disponible + Le jeu original de questions et défis + Jeu de cartes avec timer et défis + Trouvez l\'undercover avant qu\'il ne soit trop tard! + Découvrez les règles des jeux d\'ambiance populaires + + Boidelo Classic + Configuration de la partie + Nom du joueur + Joueurs: 0 / min. 3 + Pour commencer, entrez les noms des joueurs (minimum 3) + Ajouter un joueur + JOUER + Paramètres + Paramètres + Paramètres du jeu + Nombre de questions + Nombre de gorgées + Intelligence Artificielle + Activer les questions par IA + Enregistrer + Passer + Suivant + Supprimer ce joueur + + + 89++ + Le but est d\'arriver à un compte de 89 ou de le dépasser. Le joueur qui arrive/dépasse 89 perd!\n\nValet = -10 | Dame = Change sens | Roi = Choisissez la valeur\n\nÀ chaque dizaine (10, 20, 30…), distribuez des gorgées égales à la dizaine (sauf 70 = double)! + Règles du jeu + Joueurs + Timer des défis + Jouer une carte + Erreur de calcul + Compteur + ↻ Sens horaire + ↺ Sens anti-horaire + Joueur actuel + Vos cartes + Prochain défi dans: + DÉFI! + Sélectionnez la valeur (1-10): + Annoncez le compte: + Compte actuel: + DIZAINE! %d gorgées à distribuer! + 70 = DOUBLE! 14 gorgées! + Le jeu est terminé! + %s a gagné! + %s a dépassé 89 et perd! + Statistiques finales: + + Tester la connexion \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index f78bdfd..4d86866 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -19,27 +19,27 @@ @@ -98,17 +98,17 @@ @@ -119,7 +119,7 @@ @@ -128,7 +128,7 @@ @@ -137,7 +137,7 @@ @@ -147,7 +147,7 @@