package linea; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.util.ArrayList; import javax.swing.JPanel; public class ZoneDessin extends JPanel { private static final long serialVersionUID = 1L; // un booleen qui permet d'arreter l'animation (suspendre) protected boolean estArrete = false; // liste des objets graphiques private ArrayList listeObjets = new ArrayList(); // indicateur qu'une défaite (perte de contact) est survenue private boolean collisionOccur = false; // indique si le cercle a déjà été sur la ligne (début de la phase de maintien) private boolean hadBeenOnLine = false; // mode immortel - ignore les collisions private boolean immortel = false; // liste des boules bonus private ArrayList boolesBonus = new ArrayList(); // compteur pour générer les boules à intervalle régulier private int compteurBoule = 0; private int intervalleBouleBase = 165; // moins de boules sur la durée private int delaiInitialBoule = 97; // apparition initiale avancée de 3% private int niveauActuel = 1; private Double dernierJoueurY = null; // type de bonus récupéré (-1 = rouge, 1 = vert, 0 = aucun) private int bonusRecupere = 0; public ZoneDessin(){ setLayout(new BorderLayout()); setPreferredSize(new Dimension(800, 600)); setBackground(new Color(220,170,0)); } public void setCouleurFond(Color couleurFond) { setBackground(couleurFond); } public void setImmortel(boolean immortel) { this.immortel = immortel; } public void setNiveau(int niveau) { if (niveau < 1) { niveauActuel = 1; } else if (niveau > 100) { niveauActuel = 100; } else { niveauActuel = niveau; } } public int getBonusRecupere() { return bonusRecupere; } public void reinitialiserBonus() { bonusRecupere = 0; } // Ajout d'un objet graphique à la zone de dessin public void ajouterObjet(ObjetGraphique unObjet) { listeObjets.add(unObjet); } public void arreter(){ estArrete = true; } public void demarrer(){ estArrete = false; } public void traiterBoucleAnimation(){ if (estArrete==true) { return; } Cercle cercleReference = null; for (ObjetGraphique obj : listeObjets) { if (obj instanceof Cercle) { cercleReference = (Cercle) obj; break; } } double joueurY = (cercleReference != null) ? cercleReference.getY() : 300; double vitesseJoueurY = 0.0; if (dernierJoueurY != null) { vitesseJoueurY = joueurY - dernierJoueurY; } dernierJoueurY = joueurY; // --- 0. Récupérer la ligne pour synchroniser les boules --- Ligne ligneObjet = null; for (ObjetGraphique obj : listeObjets) { if (obj instanceof Ligne) { ligneObjet = (Ligne) obj; break; } } // --- 1. Générer une boule au bord droit sur la ligne --- double progression = Math.min(1.0, (niveauActuel - 1) / 25.0); int intervalleBoule = (int) Math.round(intervalleBouleBase - progression * 20.0); // ~165 -> 145 double probaVerteBase = 0.35 - progression * 0.06; // ~35% -> 29% double variationAleatoire = (Math.random() - 0.5) * 0.10; // +/- 5% double probaVerte = Math.max(0.22, Math.min(0.45, probaVerteBase + variationAleatoire)); compteurBoule++; if (compteurBoule >= intervalleBoule && ligneObjet != null) { compteurBoule = 0; boolean estVerte = Math.random() < probaVerte; double spawnX = 800; double spawnY; if (estVerte) { // Vertes: proches de la trajectoire du joueur pour être récupérables. double margeVerte = 95.0 + progression * 45.0; spawnY = joueurY + (Math.random() * (2.0 * margeVerte)) - margeVerte; } else { // Rouges: visent davantage le joueur, mais démarrent avec une marge d'esquive. double offset = 220 + progression * 20.0 + Math.random() * 150.0; spawnY = joueurY + (Math.random() < 0.5 ? -offset : offset); } if (spawnY < 20) spawnY = 20; if (spawnY > 580) spawnY = 580; if (!estVerte) { // Marge suffisante pour esquiver, sans rendre les rouges inoffensives. double distanceMin = 125.0; if (Math.abs(spawnY - joueurY) < distanceMin) { if (spawnY >= joueurY) { spawnY = Math.min(580, joueurY + distanceMin); } else { spawnY = Math.max(20, joueurY - distanceMin); } } } // On crée la boule au bord droit (800) BouleBonus boule = new BouleBonus(spawnX, spawnY, estVerte); boolesBonus.add(boule); } // --- 2. Animation des objets --- for (ObjetGraphique obj : listeObjets) obj.Animer(); // Mettre à jour et animer les boules (une seule fois par frame) if (ligneObjet != null) { for (BouleBonus boule : boolesBonus) { boule.animerAvecCible(ligneObjet.getVitesse(), joueurY, vitesseJoueurY); } } // 2. vérifier collision entre la Ligne et le Cercle (une seule passe de segments) if (ligneObjet != null && cercleReference != null) { double[] info = ligneObjet.contactInfo( cercleReference.getX(), cercleReference.getY(), cercleReference.getRayon()); boolean enContact = (info != null) && (info[1] <= cercleReference.getRayon()); double ligneY = (info != null) ? info[0] : ligneObjet.getYAuX(cercleReference.getX()); // Phase initiale en mode immortel : force d'attraction depuis le début if (!hadBeenOnLine && immortel) { double delta = ligneY - cercleReference.y; cercleReference.vitesse += delta * 0.12; // Marquer le premier contact if (enContact) { hadBeenOnLine = true; } } else if (hadBeenOnLine && immortel) { // Phase maintenance : après le premier contact, maintenir le cercle sur la ligne double rayon = cercleReference.getRayon(); double limite = ligneY - rayon; double limiteBas = ligneY + rayon; // Clamping : empêcher le cercle de sortir au-dessus if (cercleReference.y < limite) { cercleReference.y = limite; if (cercleReference.vitesse < 0) cercleReference.vitesse *= -0.3; } // Clamping : empêcher le cercle de sortir en-dessous else if (cercleReference.y > limiteBas) { cercleReference.y = limiteBas; if (cercleReference.vitesse > 0) cercleReference.vitesse *= -0.3; } } else if (info != null && !enContact && !immortel) { // Mode normal : mort si hors contact collisionOccur = true; estArrete = true; } // Synchroniser tous les autres Cercle en mode immortel if (immortel) { for (ObjetGraphique obj : listeObjets) { if (obj instanceof Cercle && obj != cercleReference) { Cercle autre = (Cercle) obj; autre.y = cercleReference.y; autre.vitesse = cercleReference.vitesse; } } } } // 3. vérifier collisions entre les boules bonus et le cercle for (int i = boolesBonus.size() - 1; i >= 0; i--) { BouleBonus boule = boolesBonus.get(i); boolean collision = false; for (ObjetGraphique obj : listeObjets) { if (obj instanceof Cercle) { Cercle c = (Cercle) obj; if (boule.collisionAvec(c)) { // Si touché : on définit le bonus et on détruit la boule bonusRecupere = boule.isVerte() ? 1 : -1; boolesBonus.remove(i); collision = true; break; } } } //4. Détruire la boule si elle dépasse le joueur sans être touchée --- // Le cercle est à X = 400. Si la boule est à X < 350, elle est "passée". if (!collision && boule.getX() < 320) { boolesBonus.remove(i); // On ne change pas bonusRecupere ici (donc rien ne se passe) } } // 5. on demande à redessiner repaint(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2D = (Graphics2D) g; g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); // Afficher tous les objets for (ObjetGraphique obj : listeObjets) { obj.Afficher(g); } // Afficher les boules bonus for (BouleBonus boule : boolesBonus) { boule.Afficher(g); } } // Indique si une collision est survenue public boolean aCollision() { return collisionOccur; } // Réinitialise l'état de la zone de dessin et supprime les objets graphiques public void reinitialiser() { listeObjets.clear(); boolesBonus.clear(); estArrete = false; collisionOccur = false; hadBeenOnLine = false; bonusRecupere = 0; compteurBoule = -delaiInitialBoule; dernierJoueurY = null; repaint(); } }