package linea; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.List; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.Timer; public class Jeu implements KeyListener, ActionListener { //------------------------------------------------------------------------- // PROPRIETES //------------------------------------------------------------------------- protected ZoneDessin ecran = new ZoneDessin(); protected Cercle demiCercleAvant; protected Cercle demiCercleArriere; protected Ligne ligne; private int _niv; protected Timer horloge; protected JFrame fenetre; protected double score = 0; protected JLabel labScore; protected JLabel labMeilleurScore; protected JLabel labNiveau; private DatabaseConnection db; private int idCompte; private int meilleurSansCompte = 0; private int mortsSansCompte = 0; private int tempsSansCompteSec = 0; private int niveauMaxAtteintSansCompte = 1; private long debutPartieMs = 0; private boolean immortel = false; private final boolean modeCampagne; private void mettreAJourNiveauMaxSansCompte(int niveau) { if (idCompte <= 0) { niveauMaxAtteintSansCompte = Math.max(niveauMaxAtteintSansCompte, niveau); } } private int meilleurActuel() { return idCompte > 0 ? db.getMeilleurScoreParCompte(idCompte) : meilleurSansCompte; } private String statsActuelles() { if (idCompte > 0) { return db.getStatsParCompte(idCompte) + "\n\n" + db.getStatsCampagneParCompte(idCompte); } return "💀 Nombre de morts: " + mortsSansCompte + "\n⏱️ Temps total: " + tempsSansCompteSec + "s" + "\n📈 Niveau max atteint: " + niveauMaxAtteintSansCompte + "\n🏆 Meilleur score: " + meilleurSansCompte; } private void enregistrerPartie(int scoreActuel, int tempsPartieSec) { if (idCompte > 0) { db.sauvegarderScore(scoreActuel, idCompte, 1, tempsPartieSec); return; } meilleurSansCompte = Math.max(meilleurSansCompte, scoreActuel); mortsSansCompte += 1; tempsSansCompteSec += tempsPartieSec; } //------------------------------------------------------------------------- // CONSTRUCTEUR //------------------------------------------------------------------------- public Jeu(DatabaseConnection db, int idCompte, int niveau) { this(db, idCompte, niveau, false); } public Jeu(DatabaseConnection db, int idCompte, int niveau, boolean modeCampagne) { this.db = db; this.idCompte = idCompte; this._niv = niveau; this.modeCampagne = modeCampagne; mettreAJourNiveauMaxSansCompte(_niv); labScore = new JLabel(); labScore.setText("

score : 0

"); labMeilleurScore = new JLabel(); labNiveau = new JLabel(); JPanel panneauScores = new JPanel(new FlowLayout(FlowLayout.LEFT, 20, 0)); panneauScores.setOpaque(false); panneauScores.add(labScore); panneauScores.add(labMeilleurScore); panneauScores.add(labNiveau); ecran.add(panneauScores, BorderLayout.NORTH); rafraichirMeilleurScore(); rafraichirNiveau(); } private void rafraichirMeilleurScore() { labMeilleurScore.setText("

meilleur : " + meilleurActuel() + "

"); } private void rafraichirNiveau() { labNiveau.setText("

niveau : " + _niv + "

"); } private void rafraichirTitreFenetre() { if (fenetre == null) { return; } String titre = modeCampagne ? "Linea Game [CAMPAGNE]" : "Linea Game"; fenetre.setTitle(titre + (immortel ? " [MODE IMMORTEL 🛡️]" : "")); } private String categorieNiveau(int niveau) { if (niveau <= 2) return "Facile"; if (niveau <= 4) return "Moyen"; if (niveau <= 6) return "Difficile"; if (niveau <= 8) return "Tres Difficile"; if (niveau <= 10) return "Expert"; if (niveau <= 12) return "Cauchemar"; if (niveau <= 14) return "Chaos"; if (niveau <= 16) return "Infernal"; if (niveau <= 18) return "Apocalypse"; if (niveau <= 20) return "Extreme"; if (niveau <= 30) return "Infini"; if (niveau <= 50) return "Au-dela"; return "Cosmos"; } private void appliquerThemeNiveau() { String categorie = categorieNiveau(_niv); Color fond; Color couleurLigne; Color couleurAvant; Color couleurArriere; Color couleurTexte; switch (categorie) { case "Facile": fond = new Color(225, 245, 225); couleurLigne = new Color(45, 120, 45); couleurAvant = new Color(55, 185, 80); couleurArriere = new Color(175, 70, 70); couleurTexte = new Color(20, 50, 20); break; case "Moyen": fond = new Color(215, 235, 250); couleurLigne = new Color(40, 95, 150); couleurAvant = new Color(50, 165, 120); couleurArriere = new Color(185, 80, 85); couleurTexte = new Color(20, 45, 80); break; case "Difficile": fond = new Color(250, 235, 200); couleurLigne = new Color(155, 95, 40); couleurAvant = new Color(195, 145, 55); couleurArriere = new Color(175, 70, 40); couleurTexte = new Color(85, 55, 20); break; case "Tres Difficile": fond = new Color(245, 215, 195); couleurLigne = new Color(145, 70, 40); couleurAvant = new Color(185, 110, 65); couleurArriere = new Color(175, 55, 45); couleurTexte = new Color(80, 35, 25); break; case "Expert": fond = new Color(210, 190, 235); couleurLigne = new Color(105, 55, 160); couleurAvant = new Color(130, 85, 200); couleurArriere = new Color(170, 60, 95); couleurTexte = new Color(45, 20, 70); break; case "Cauchemar": fond = new Color(175, 160, 210); couleurLigne = new Color(85, 65, 150); couleurAvant = new Color(120, 90, 190); couleurArriere = new Color(155, 55, 110); couleurTexte = new Color(245, 245, 245); break; case "Chaos": fond = new Color(205, 120, 120); couleurLigne = new Color(170, 40, 45); couleurAvant = new Color(225, 115, 40); couleurArriere = new Color(200, 30, 60); couleurTexte = new Color(245, 245, 245); break; case "Infernal": fond = new Color(165, 70, 55); couleurLigne = new Color(190, 35, 25); couleurAvant = new Color(235, 95, 25); couleurArriere = new Color(210, 25, 20); couleurTexte = new Color(245, 245, 245); break; case "Apocalypse": fond = new Color(120, 40, 45); couleurLigne = new Color(210, 30, 40); couleurAvant = new Color(245, 95, 35); couleurArriere = new Color(225, 20, 35); couleurTexte = new Color(245, 245, 245); break; case "Extreme": fond = new Color(70, 70, 70); couleurLigne = new Color(225, 225, 225); couleurAvant = new Color(255, 235, 70); couleurArriere = new Color(240, 80, 80); couleurTexte = new Color(245, 245, 245); break; case "Infini": fond = new Color(35, 55, 85); couleurLigne = new Color(95, 170, 255); couleurAvant = new Color(70, 220, 255); couleurArriere = new Color(180, 80, 255); couleurTexte = new Color(245, 245, 245); break; case "Au-dela": fond = new Color(20, 30, 70); couleurLigne = new Color(120, 155, 255); couleurAvant = new Color(80, 205, 255); couleurArriere = new Color(205, 95, 255); couleurTexte = new Color(245, 245, 245); break; default: fond = new Color(10, 15, 45); couleurLigne = new Color(140, 120, 255); couleurAvant = new Color(120, 225, 255); couleurArriere = new Color(235, 95, 255); couleurTexte = new Color(245, 245, 245); break; } ecran.setCouleurFond(fond); ligne.setCouleurLigne(couleurLigne); demiCercleAvant.setCouleur(couleurAvant); demiCercleArriere.setCouleur(couleurArriere); labScore.setForeground(couleurTexte); labMeilleurScore.setForeground(couleurTexte); labNiveau.setForeground(couleurTexte); } private boolean choisirNouveauCompte() { List pseudos = new ArrayList<>(db.getPseudos()); pseudos.add(0, "Sans compte"); String choix = (String) JOptionPane.showInputDialog( null, "Choisissez le compte pour la prochaine partie :", "Changer de compte", JOptionPane.QUESTION_MESSAGE, null, pseudos.toArray(String[]::new), pseudos.get(0) ); if (choix == null) { return false; } if ("Sans compte".equals(choix)) { idCompte = -1; meilleurSansCompte = 0; mortsSansCompte = 0; tempsSansCompteSec = 0; niveauMaxAtteintSansCompte = Math.max(1, _niv); } else { idCompte = db.getIdParPseudo(choix); } rafraichirMeilleurScore(); return true; } private int choisirNouveauNiveau() { // Générer les niveaux dynamiquement jusqu'à 100 String[] niveaux = new String[100]; for (int i = 1; i <= 100; i++) { niveaux[i - 1] = genererLabelNiveau(i); } JList list = new JList<>(niveaux); list.setSelectedIndex(_niv - 1); list.setVisibleRowCount(15); JScrollPane scrollPane = new JScrollPane(list); scrollPane.setPreferredSize(new java.awt.Dimension(350, 350)); int result = JOptionPane.showConfirmDialog( null, scrollPane, "🎮 Sélection du Niveau - Linea", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE ); if (result == JOptionPane.OK_OPTION) { return list.getSelectedIndex() + 1; } return -1; } private String genererLabelNiveau(int niveau) { if (niveau <= 2) { return "⭐ Niveau " + niveau + " - Facile" + (niveau == 2 ? "+" : ""); } else if (niveau <= 4) { return "⭐⭐ Niveau " + niveau + " - Moyen" + (niveau == 4 ? "+" : ""); } else if (niveau <= 6) { return "⭐⭐⭐ Niveau " + niveau + " - Difficile" + (niveau == 6 ? "+" : ""); } else if (niveau <= 8) { return "⭐⭐⭐⭐ Niveau " + niveau + " - Très Difficile" + (niveau == 8 ? "+" : ""); } else if (niveau <= 10) { return "⭐⭐⭐⭐⭐ Niveau " + niveau + " - Expert" + (niveau == 10 ? "+" : ""); } else if (niveau <= 12) { return "🔥 Niveau " + niveau + " - Cauchemar" + (niveau == 12 ? "+" : ""); } else if (niveau <= 14) { return "🔥 Niveau " + niveau + " - Chaos" + (niveau == 14 ? "+" : ""); } else if (niveau <= 16) { return "💀 Niveau " + niveau + " - Infernal" + (niveau == 16 ? "+" : ""); } else if (niveau <= 18) { return "💀 Niveau " + niveau + " - Apocalypse" + (niveau == 18 ? "+" : ""); } else if (niveau <= 20) { return "⚡ Niveau " + niveau + " - Extrême" + (niveau == 20 ? "+" : ""); } else if (niveau <= 30) { return "⚡ Niveau " + niveau + " - Infini"; } else if (niveau <= 50) { return "∞ Niveau " + niveau + " - Au-delà"; } else { return "🌌 Niveau " + niveau + " - Cosmos"; } } private void choisirModeImmortel() { int result = JOptionPane.showConfirmDialog( null, "Activer le mode IMMORTEL ? 🛡️\n(Vous ne pourrez pas mourir)", "Mode Immortel", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE ); immortel = (result == JOptionPane.YES_OPTION); } //------------------------------------------------------------------------- // METHODES DE GESTION DU JEU //------------------------------------------------------------------------- /** * Centralise la création et la configuration des objets du jeu. * Évite de répéter ce code dans demarrer() et resetLevel(). */ private void initialiserPartie() { // 1. Nettoyage de l'écran ecran.reinitialiser(); // 2. Création des instances demiCercleAvant = new Cercle(90, -180); demiCercleArriere = new Cercle(90, 180); ligne = new Ligne(_niv); ecran.setNiveau(_niv); // 3. En mode immortel, positionner les cercles loin (la force les attirera vers la ligne) // En mode normal, ils restent au centre (y = 200 par défaut du constructeur) if (immortel) { demiCercleAvant.y = -300; demiCercleArriere.y = 300; } // 4. Configuration visuelle appliquerThemeNiveau(); // 5. Ajout à l'écran (l'ordre définit la superposition) ecran.ajouterObjet(demiCercleArriere); ecran.ajouterObjet(ligne); ecran.ajouterObjet(demiCercleAvant); rafraichirNiveau(); } public void demarrer() { // Demander si le mode immortel est activé choisirModeImmortel(); // Passer le flag immortel à l'écran ecran.setImmortel(immortel); fenetre = new JFrame(); rafraichirTitreFenetre(); // Initialise les objets une première fois initialiserPartie(); ecran.addKeyListener(this); ecran.setFocusable(true); fenetre.setContentPane(ecran); fenetre.pack(); fenetre.setLocation(100, 100); fenetre.setVisible(true); fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Création et lancement du timer horloge = new Timer(40, this); horloge.start(); debutPartieMs = System.currentTimeMillis(); } private void resetLevel() { // Réinitialisation des variables de jeu score = 0; labScore.setText("

score : 0

"); // Passer le flag immortel à l'écran ecran.setImmortel(immortel); rafraichirTitreFenetre(); // Ré-initialisation des objets graphiques initialiserPartie(); debutPartieMs = System.currentTimeMillis(); // Relance le timer existant et redonne le focus if (horloge != null) horloge.restart(); ecran.requestFocusInWindow(); } //------------------------------------------------------------------------- // EVENEMENTS (Timer & Clavier) //------------------------------------------------------------------------- @Override public void actionPerformed(ActionEvent e) { ecran.traiterBoucleAnimation(); // Vérifier si un bonus a été récupéré int bonus = ecran.getBonusRecupere(); if (bonus != 0) { ecran.reinitialiserBonus(); int niveauActuel = _niv; int nouveauNiveau = _niv + bonus; // Limiter le niveau entre 1 et 100 if (nouveauNiveau < 1) { nouveauNiveau = 1; } else if (nouveauNiveau > 100) { nouveauNiveau = 100; } if (modeCampagne && idCompte > 0 && bonus > 0) { db.debloquerNiveauCampagne(idCompte, niveauActuel); } _niv = nouveauNiveau; mettreAJourNiveauMaxSansCompte(_niv); resetLevel(); return; } if (ecran.aCollision() && !immortel) { horloge.stop(); int scoreActuel = (int) score; int tempsPartieSec = (int) ((System.currentTimeMillis() - debutPartieMs) / 1000L); enregistrerPartie(scoreActuel, tempsPartieSec); rafraichirMeilleurScore(); while (true) { Object[] options = modeCampagne ? new Object[]{"▶️ Relancer", "📊 Statistiques", "❌ Quitter"} : new Object[]{"▶️ Relancer", "👤 Compte", "📈 Niveau", "📊 Statistiques", "❌ Quitter"}; String message = "🏁 GAME OVER\n\n" + "Score: " + scoreActuel + "\n" + "Meilleur: " + meilleurActuel() + "\n" + "Durée: " + tempsPartieSec + "s"; int choix = JOptionPane.showOptionDialog(null, message, "🏁 Fin de Partie", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]); if (choix == 0) { int modeRelance = JOptionPane.showConfirmDialog( null, "Relancer en mode immortel ?", "Relancer", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE ); if (modeRelance == JOptionPane.CANCEL_OPTION || modeRelance == JOptionPane.CLOSED_OPTION) { continue; } immortel = (modeRelance == JOptionPane.YES_OPTION); ecran.setImmortel(immortel); resetLevel(); break; } if (modeCampagne) { if (choix == 1) { JOptionPane.showMessageDialog(null, statsActuelles(), "📊 Statistiques", JOptionPane.INFORMATION_MESSAGE); continue; } System.exit(0); } if (choix == 1) { if (choisirNouveauCompte()) { resetLevel(); break; } continue; } if (choix == 2) { int nouveauNiveau = choisirNouveauNiveau(); if (nouveauNiveau > 0) { _niv = nouveauNiveau; mettreAJourNiveauMaxSansCompte(_niv); resetLevel(); break; } continue; } if (choix == 3) { JOptionPane.showMessageDialog(null, statsActuelles(), "📊 Statistiques", JOptionPane.INFORMATION_MESSAGE); continue; } System.exit(0); } return; } // Calcul du score basé sur le temps écoulé score += (0.05 + (0.05 * horloge.getDelay() / 100.0)); labScore.setText("

score : " + (int)score + "

"); } @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_UP) { demiCercleAvant.Monter(); demiCercleArriere.Monter(); } } @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_UP) { demiCercleAvant.ArreterMonter(); demiCercleArriere.ArreterMonter(); } } @Override public void keyTyped(KeyEvent e) {} }