diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..edfe819 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "java.project.referencedLibraries": [ + "Projet-Dev/lib/**/*.jar" + ], + "java.configuration.runtimes": [ + { + "name": "JavaSE-24", + "path": "C:\\Program Files\\Java\\jdk-24", + "default": true + } + ], + "java.jdt.ls.java.home": "C:\\Program Files\\Java\\jdk-24" +} \ No newline at end of file diff --git a/Jeu.db b/Jeu.db new file mode 100644 index 0000000..ae846f7 Binary files /dev/null and b/Jeu.db differ diff --git a/lib/slf4j-api-2.0.13.jar b/lib/slf4j-api-2.0.13.jar new file mode 100644 index 0000000..a800cc2 Binary files /dev/null and b/lib/slf4j-api-2.0.13.jar differ diff --git a/lib/slf4j-simple-2.0.13.jar b/lib/slf4j-simple-2.0.13.jar new file mode 100644 index 0000000..b756f92 Binary files /dev/null and b/lib/slf4j-simple-2.0.13.jar differ diff --git a/lib/sqlite-jdbc-3.45.1.0.jar b/lib/sqlite-jdbc-3.45.1.0.jar new file mode 100644 index 0000000..6a484ac Binary files /dev/null and b/lib/sqlite-jdbc-3.45.1.0.jar differ diff --git a/linea/DatabaseConnection.java b/linea/DatabaseConnection.java new file mode 100644 index 0000000..9f3d922 --- /dev/null +++ b/linea/DatabaseConnection.java @@ -0,0 +1,211 @@ +package linea; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + + +public class DatabaseConnection { + + private static final Path DB_PATH = resolveDbPath(); + private static final String DB_URL = "jdbc:sqlite:" + DB_PATH; + private Connection conn; + + private static Path resolveDbPath() { + Path cwd = Paths.get("").toAbsolutePath().normalize(); + Path inProjetDev = cwd.resolve("Projet-Dev").resolve("Jeu.db"); + if (Files.exists(cwd.resolve("Projet-Dev"))) { + return inProjetDev; + } + return cwd.resolve("Jeu.db"); + } + + public void connect() { + try { + Path parent = DB_PATH.getParent(); + if (parent != null) { + Files.createDirectories(parent); + } + conn = DriverManager.getConnection(DB_URL); + System.out.println("Connexion à la DB OK: " + DB_PATH); + } catch (SQLException e) { + System.err.println("Erreur de connexion DB: " + e.getMessage()); + } catch (Exception e) { + System.err.println("Erreur accès fichier DB: " + e.getMessage()); + } + } + + public void disconnect() { + if (conn == null) { + return; + } + try { + if (!conn.isClosed()) { + conn.close(); + System.out.println("Déconnexion DB OK"); + } + } catch (SQLException e) { + System.err.println("Erreur à la fermeture DB: " + e.getMessage()); + } + } + + public void createTables() { + if (conn == null) { + System.err.println("DB non connectée, impossible de créer les tables."); + return; + } + + String createCompte = """ + CREATE TABLE IF NOT EXISTS Compte ( + id_compte INTEGER PRIMARY KEY AUTOINCREMENT, + pseudo TEXT NOT NULL + ); + """; + + String createNiveau = """ + CREATE TABLE IF NOT EXISTS Niveau ( + id_niveau INTEGER PRIMARY KEY AUTOINCREMENT, + nom TEXT, + nb_Objet INTEGER NOT NULL + ); + """; + + String createScore = """ + CREATE TABLE IF NOT EXISTS Score ( + id_score INTEGER PRIMARY KEY AUTOINCREMENT, + valeur_score INTEGER, + nb_mort INTEGER, + temps_jeu INTEGER, + id_compte INTEGER, + id_niveau INTEGER, + FOREIGN KEY(id_compte) REFERENCES Compte(id_compte), + FOREIGN KEY(id_niveau) REFERENCES Niveau(id_niveau) + ); + """; + + try (Statement stmt = conn.createStatement()) { + stmt.executeUpdate(createCompte); + stmt.executeUpdate(createNiveau); + stmt.executeUpdate(createScore); + System.out.println("Tables créées / existantes OK"); + } catch (SQLException e) { + System.err.println("Erreur création tables : " + e.getMessage()); + } + } + + public void sauvegarderScore(int valeur, int idCompte, int nbMort, int tempsJeuSec) { + if (conn == null || idCompte <= 0) return; + String sql = "INSERT INTO Score (valeur_score, nb_mort, temps_jeu, id_compte) VALUES (?, ?, ?, ?)"; + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setInt(1, valeur); + ps.setInt(2, nbMort); + ps.setInt(3, tempsJeuSec); + ps.setInt(4, idCompte); + ps.executeUpdate(); + } catch (SQLException e) { + System.err.println("Erreur sauvegarde score : " + e.getMessage()); + } + } + + public int getMeilleurScoreParCompte(int idCompte) { + if (conn == null || idCompte <= 0) return 0; + String sql = "SELECT MAX(valeur_score) FROM Score WHERE id_compte = ?"; + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setInt(1, idCompte); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) return rs.getInt(1); + } + } catch (SQLException e) { + System.err.println("Erreur lecture meilleur score : " + e.getMessage()); + } + return 0; + } + + public int creerCompte(String pseudo) { + if (conn == null || pseudo == null || pseudo.isBlank()) return -1; + String sql = "INSERT INTO Compte (pseudo) VALUES (?)"; + try (PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { + ps.setString(1, pseudo.trim()); + ps.executeUpdate(); + try (ResultSet rs = ps.getGeneratedKeys()) { + if (rs.next()) return rs.getInt(1); + } + } catch (SQLException e) { + System.err.println("Erreur création compte : " + e.getMessage()); + } + return -1; + } + + public int getIdParPseudo(String pseudo) { + if (conn == null || pseudo == null || pseudo.isBlank()) return -1; + String sql = "SELECT id_compte FROM Compte WHERE pseudo = ? LIMIT 1"; + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, pseudo.trim()); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) return rs.getInt(1); + } + } catch (SQLException e) { + System.err.println("Erreur lecture compte : " + e.getMessage()); + } + return -1; + } + + public void supprimerCompte(int idCompte) { + if (conn == null || idCompte <= 0) return; + try (PreparedStatement ps1 = conn.prepareStatement("DELETE FROM Score WHERE id_compte = ?"); + PreparedStatement ps2 = conn.prepareStatement("DELETE FROM Compte WHERE id_compte = ?")) { + ps1.setInt(1, idCompte); + ps1.executeUpdate(); + ps2.setInt(1, idCompte); + ps2.executeUpdate(); + } catch (SQLException e) { + System.err.println("Erreur suppression compte : " + e.getMessage()); + } + } + + public List getPseudos() { + List pseudos = new ArrayList<>(); + if (conn == null) return pseudos; + String sql = "SELECT pseudo FROM Compte ORDER BY pseudo"; + try (Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + pseudos.add(rs.getString("pseudo")); + } + } catch (SQLException e) { + System.err.println("Erreur lecture comptes : " + e.getMessage()); + } + return pseudos; + } + + public String getStatsParCompte(int idCompte) { + if (conn == null || idCompte <= 0) { + return "Aucune statistique disponible."; + } + + String sql = "SELECT COALESCE(SUM(nb_mort),0) AS morts, " + + "COALESCE(SUM(temps_jeu),0) AS temps, " + + "COALESCE(MAX(valeur_score),0) AS meilleur " + + "FROM Score WHERE id_compte = ?"; + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setInt(1, idCompte); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + int morts = rs.getInt("morts"); + int temps = rs.getInt("temps"); + int meilleur = rs.getInt("meilleur"); + return "Nombre de morts : " + morts + + "\nTemps de jeu total : " + temps + " s" + + "\nMeilleur score : " + meilleur; + } + } + } catch (SQLException e) { + return "Erreur lecture stats : " + e.getMessage(); + } + return "Aucune statistique disponible."; + } +} + diff --git a/linea/Jeu.db b/linea/Jeu.db new file mode 100644 index 0000000..17e7c0e Binary files /dev/null and b/linea/Jeu.db differ diff --git a/linea/Jeu.java b/linea/Jeu.java index c54457c..7061980 100644 --- a/linea/Jeu.java +++ b/linea/Jeu.java @@ -1,14 +1,19 @@ 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.Timer; import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.Timer; public class Jeu implements KeyListener, ActionListener { @@ -24,15 +29,88 @@ public class Jeu implements KeyListener, ActionListener { protected Timer horloge; protected double score = 0; protected JLabel labScore; + protected JLabel labMeilleurScore; + private DatabaseConnection db; + private int idCompte; + private int meilleurSansCompte = 0; + private int mortsSansCompte = 0; + private int tempsSansCompteSec = 0; + private long debutPartieMs = 0; + + private int meilleurActuel() { + return idCompte > 0 ? db.getMeilleurScoreParCompte(idCompte) : meilleurSansCompte; + } + + private String statsActuelles() { + if (idCompte > 0) { + return db.getStatsParCompte(idCompte); + } + return "Nombre de morts : " + mortsSansCompte + + "\nTemps de jeu total : " + tempsSansCompteSec + " s" + + "\nMeilleur 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() { + public Jeu(DatabaseConnection db, int idCompte) { + this.db = db; + this.idCompte = idCompte; labScore = new JLabel(); labScore.setText("

score : 0

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

meilleur : " + meilleurActuel() + "

"); + } + + 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; + } else { + idCompte = db.getIdParPseudo(choix); + } + rafraichirMeilleurScore(); + return true; } //------------------------------------------------------------------------- @@ -79,6 +157,7 @@ public class Jeu implements KeyListener, ActionListener { // Création et lancement du timer horloge = new Timer(40, this); horloge.start(); + debutPartieMs = System.currentTimeMillis(); } private void resetLevel() { @@ -88,6 +167,7 @@ public class Jeu implements KeyListener, ActionListener { // Ré-initialisation des objets graphiques initialiserPartie(); + debutPartieMs = System.currentTimeMillis(); // Relance le timer existant et redonne le focus if (horloge != null) horloge.restart(); @@ -104,17 +184,39 @@ public class Jeu implements KeyListener, ActionListener { if (ecran.aCollision()) { horloge.stop(); - - Object[] options = {"Relancer", "Quitter"}; - int choix = JOptionPane.showOptionDialog(null, - "Perdu\nScore : " + (int)score, - "Game Over", - JOptionPane.DEFAULT_OPTION, - JOptionPane.INFORMATION_MESSAGE, - null, options, options[0]); - if (choix == 0) resetLevel(); - else System.exit(0); + int scoreActuel = (int) score; + int tempsPartieSec = (int) ((System.currentTimeMillis() - debutPartieMs) / 1000L); + enregistrerPartie(scoreActuel, tempsPartieSec); + + rafraichirMeilleurScore(); + + while (true) { + Object[] options = {"Relancer", "Changer de compte", "Voir stats", "Quitter"}; + int choix = JOptionPane.showOptionDialog(null, + "Perdu\nScore : " + scoreActuel + "\nMeilleur : " + meilleurActuel(), + "Game Over", + JOptionPane.DEFAULT_OPTION, + JOptionPane.INFORMATION_MESSAGE, + null, options, options[0]); + + if (choix == 0) { + resetLevel(); + break; + } + if (choix == 1) { + if (choisirNouveauCompte()) { + resetLevel(); + break; + } + continue; + } + if (choix == 2) { + JOptionPane.showMessageDialog(null, statsActuelles(), "Statistiques", JOptionPane.INFORMATION_MESSAGE); + continue; + } + System.exit(0); + } return; } diff --git a/linea/LineaAppli.java b/linea/LineaAppli.java index 61bd22b..e2ff307 100644 --- a/linea/LineaAppli.java +++ b/linea/LineaAppli.java @@ -1,13 +1,109 @@ package linea; +import java.util.List; +import javax.swing.JOptionPane; +import javax.swing.UIManager; + public class LineaAppli { + + private static String choisirPseudo(DatabaseConnection db, String message, int messageType) { + List pseudos = db.getPseudos(); + if (pseudos.isEmpty()) { + JOptionPane.showMessageDialog(null, "Aucun compte disponible."); + return null; + } + + return (String) JOptionPane.showInputDialog( + null, + message, + "Comptes", + messageType, + null, + pseudos.toArray(String[]::new), + pseudos.get(0) + ); + } + + private static Integer menuComptes(DatabaseConnection db) { + while (true) { + Object[] options = {"Sélectionner", "Créer", "Supprimer", "Retour"}; + int choix = JOptionPane.showOptionDialog( + null, + "Gestion des comptes :", + "Comptes", + JOptionPane.DEFAULT_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[0] + ); + + if (choix == JOptionPane.CLOSED_OPTION || choix == 3) return null; + + switch (choix) { + case 0 -> { + String pseudo = choisirPseudo(db, "Sélectionnez un compte :", JOptionPane.QUESTION_MESSAGE); + if (pseudo != null) return db.getIdParPseudo(pseudo); + } + case 1 -> { + String pseudo = JOptionPane.showInputDialog(null, "Nouveau pseudo :"); + if (pseudo == null || pseudo.trim().isEmpty()) continue; + pseudo = pseudo.trim(); + int id = db.getIdParPseudo(pseudo); + return id > 0 ? id : db.creerCompte(pseudo); + } + case 2 -> { + String pseudo = choisirPseudo(db, "Sélectionnez le compte à supprimer :", JOptionPane.WARNING_MESSAGE); + if (pseudo == null) continue; + int confirm = JOptionPane.showConfirmDialog( + null, + "Supprimer le compte \"" + pseudo + "\" et toute sa progression ?", + "Confirmation", + JOptionPane.YES_NO_OPTION + ); + if (confirm == JOptionPane.YES_OPTION) { + int id = db.getIdParPseudo(pseudo); + if (id > 0) db.supprimerCompte(id); + } + } + } + } + } + //------------------------------------------------------------------------- // Classe de base de l'application, rien à modifier ici //------------------------------------------------------------------------- public static void main(String[] arg) { + UIManager.put("OptionPane.cancelButtonText", "Retour"); - Jeu jeu = new Jeu(); - - jeu.demarrer(); + DatabaseConnection db = new DatabaseConnection(); + db.connect(); + db.createTables(); + + while (true) { + Object[] options = {"Comptes", "Sans compte", "Quitter"}; + int choix = JOptionPane.showOptionDialog(null, + "Choisissez une action :", + "Menu", JOptionPane.DEFAULT_OPTION, + JOptionPane.QUESTION_MESSAGE, null, options, options[0]); + + if (choix == JOptionPane.CLOSED_OPTION || choix == 2) { + return; + } + + switch (choix) { + case 1 -> { + new Jeu(db, -1).demarrer(); + return; + } + case 0 -> { + Integer idCompte = menuComptes(db); + if (idCompte != null) { + new Jeu(db, idCompte).demarrer(); + return; + } + } + } + } } } diff --git a/out/linea/Cercle.class b/out/linea/Cercle.class new file mode 100644 index 0000000..4e7f37a Binary files /dev/null and b/out/linea/Cercle.class differ diff --git a/out/linea/DatabaseConnection.class b/out/linea/DatabaseConnection.class new file mode 100644 index 0000000..d58aab8 Binary files /dev/null and b/out/linea/DatabaseConnection.class differ diff --git a/out/linea/Jeu.class b/out/linea/Jeu.class new file mode 100644 index 0000000..d8afe94 Binary files /dev/null and b/out/linea/Jeu.class differ diff --git a/out/linea/Ligne.class b/out/linea/Ligne.class new file mode 100644 index 0000000..a143bec Binary files /dev/null and b/out/linea/Ligne.class differ diff --git a/out/linea/LineaAppli.class b/out/linea/LineaAppli.class new file mode 100644 index 0000000..3b076c5 Binary files /dev/null and b/out/linea/LineaAppli.class differ diff --git a/out/linea/ObjetGraphique.class b/out/linea/ObjetGraphique.class new file mode 100644 index 0000000..6d6b1d0 Binary files /dev/null and b/out/linea/ObjetGraphique.class differ diff --git a/out/linea/Segment.class b/out/linea/Segment.class new file mode 100644 index 0000000..fa1e001 Binary files /dev/null and b/out/linea/Segment.class differ diff --git a/out/linea/ZoneDessin.class b/out/linea/ZoneDessin.class new file mode 100644 index 0000000..827b01f Binary files /dev/null and b/out/linea/ZoneDessin.class differ