2026-03-28 08:16:44 +01:00
|
|
|
package linea;
|
|
|
|
|
|
|
|
|
|
import java.awt.BasicStroke;
|
|
|
|
|
import java.awt.Color;
|
|
|
|
|
import java.awt.Graphics;
|
|
|
|
|
import java.awt.Graphics2D;
|
|
|
|
|
|
|
|
|
|
public class BouleBonus extends ObjetGraphique {
|
|
|
|
|
|
|
|
|
|
private double rayon = 15;
|
|
|
|
|
private double vitesse = 5.0;
|
|
|
|
|
private boolean estVerte; // true = bonus (niveau +1), false = malus (niveau -1)
|
|
|
|
|
private int frameCounter = 0;
|
2026-03-28 14:18:17 +01:00
|
|
|
private double vitesseVerticale = 0.0;
|
|
|
|
|
private double amplitudeOndulation = 0.2;
|
|
|
|
|
private double vitesseHorizontaleLisse = 0.0;
|
|
|
|
|
private double phaseOndulation = Math.random() * Math.PI * 2; // phase unique par boule
|
2026-03-28 17:19:34 +01:00
|
|
|
private double sensEvitementLigne = 0.0;
|
2026-03-28 14:18:17 +01:00
|
|
|
|
|
|
|
|
private static double clamp(double value, double min, double max) {
|
|
|
|
|
return Math.max(min, Math.min(max, value));
|
|
|
|
|
}
|
2026-03-28 08:16:44 +01:00
|
|
|
|
|
|
|
|
public BouleBonus(double x, double y, boolean estVerte) {
|
|
|
|
|
this.x = x;
|
|
|
|
|
this.y = y;
|
|
|
|
|
this.estVerte = estVerte;
|
|
|
|
|
|
|
|
|
|
if (estVerte) {
|
|
|
|
|
this.couleur = new Color(0.0f, 0.8f, 0.0f); // Vert
|
2026-03-28 14:18:17 +01:00
|
|
|
this.rayon = 15;
|
|
|
|
|
this.amplitudeOndulation = 0.45; // onde visible plus ample
|
|
|
|
|
this.vitesseHorizontaleLisse = 4.8;
|
2026-03-28 08:16:44 +01:00
|
|
|
} else {
|
|
|
|
|
this.couleur = new Color(0.8f, 0.0f, 0.0f); // Rouge
|
2026-03-28 14:18:17 +01:00
|
|
|
this.rayon = 14;
|
|
|
|
|
this.amplitudeOndulation = 0.14;
|
|
|
|
|
this.vitesseHorizontaleLisse = 4.3;
|
2026-03-28 17:19:34 +01:00
|
|
|
this.sensEvitementLigne = Math.random() < 0.5 ? -1.0 : 1.0;
|
2026-03-28 08:16:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public double getRayon() {
|
|
|
|
|
return rayon;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isVerte() {
|
|
|
|
|
return estVerte;
|
|
|
|
|
}
|
2026-03-28 17:19:34 +01:00
|
|
|
|
|
|
|
|
public void garantirDistanceLigne(double yLigne, double margePx) {
|
|
|
|
|
if (estVerte) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double distanceMiniCentre = rayon + Math.max(0.0, margePx);
|
|
|
|
|
double ecart = y - yLigne;
|
|
|
|
|
double distance = Math.abs(ecart);
|
|
|
|
|
if (distance >= distanceMiniCentre) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double sens = (ecart == 0.0) ? sensEvitementLigne : Math.signum(ecart);
|
|
|
|
|
y = yLigne + sens * distanceMiniCentre;
|
|
|
|
|
vitesseVerticale = 0.0;
|
|
|
|
|
|
|
|
|
|
if (y < 20) y = 20;
|
|
|
|
|
if (y > 580) y = 580;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ajusterVitessePourDistanceLigne(double yLigne, double margePx) {
|
|
|
|
|
if (estVerte) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double distanceCibleCentre = rayon + Math.max(0.0, margePx);
|
|
|
|
|
double ecart = y - yLigne;
|
|
|
|
|
double distance = Math.abs(ecart);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double progressionZone = clamp((560.0 - x) / 240.0, 0.0, 1.0);
|
|
|
|
|
if (progressionZone <= 0.0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double sens = (ecart == 0.0) ? sensEvitementLigne : Math.signum(ecart);
|
|
|
|
|
|
|
|
|
|
// vitesse variable
|
|
|
|
|
if (distance > distanceCibleCentre) {
|
|
|
|
|
double excedent = distance - distanceCibleCentre;
|
|
|
|
|
double pas = clamp(excedent * (0.08 + progressionZone * 0.20), 0.0, 10.0 + progressionZone * 8.0);
|
|
|
|
|
y -= sens * pas;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (y < 20) y = 20;
|
|
|
|
|
if (y > 580) y = 580;
|
|
|
|
|
}
|
2026-03-28 08:16:44 +01:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
void Afficher(Graphics g) {
|
|
|
|
|
Graphics2D g2D = (Graphics2D) g;
|
|
|
|
|
|
2026-03-28 17:19:34 +01:00
|
|
|
double rayonAffiche = rayon;
|
2026-03-28 08:16:44 +01:00
|
|
|
|
2026-03-28 17:19:34 +01:00
|
|
|
// Dessiner le cercle rempli
|
2026-03-28 08:16:44 +01:00
|
|
|
g2D.setColor(couleur);
|
2026-03-28 14:18:17 +01:00
|
|
|
g2D.fillOval((int)(x - rayonAffiche), (int)(y - rayonAffiche),
|
|
|
|
|
(int)(2 * rayonAffiche), (int)(2 * rayonAffiche));
|
2026-03-28 08:16:44 +01:00
|
|
|
|
|
|
|
|
// Contour
|
|
|
|
|
g2D.setStroke(new BasicStroke(2.0f));
|
|
|
|
|
g2D.setColor(estVerte ? new Color(0.0f, 1.0f, 0.0f) : new Color(1.0f, 0.0f, 0.0f));
|
2026-03-28 14:18:17 +01:00
|
|
|
g2D.drawOval((int)(x - rayonAffiche), (int)(y - rayonAffiche),
|
|
|
|
|
(int)(2 * rayonAffiche), (int)(2 * rayonAffiche));
|
2026-03-28 08:16:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
void Animer() {
|
2026-03-28 17:19:34 +01:00
|
|
|
// Déplacement vers la gauche
|
2026-03-28 08:16:44 +01:00
|
|
|
x -= vitesse;
|
|
|
|
|
}
|
2026-03-28 14:18:17 +01:00
|
|
|
|
|
|
|
|
public void animerAvecCible(double vitesseLigne, double cibleY) {
|
|
|
|
|
animerAvecCible(vitesseLigne, cibleY, 0.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void animerAvecCible(double vitesseLigne, double cibleY, double cibleVitesseY) {
|
|
|
|
|
// Vertes: trajectoire fluide à intercepter. Rouges: poursuite plus nette avec fenêtre d'esquive.
|
|
|
|
|
double facteurHorizontal = estVerte ? 0.88 : 0.64;
|
|
|
|
|
double vitesseMax = estVerte ? 9.2 : 5.8;
|
|
|
|
|
double vitessePoursuite = estVerte ? 0.075 : 0.050; // meilleure réactivité
|
|
|
|
|
double limiteVerticale = estVerte ? 1.85 : 1.05; // accélération verticale
|
|
|
|
|
double amortissement = estVerte ? 0.87 : 0.81; // moins de friction
|
2026-03-28 17:19:34 +01:00
|
|
|
double zoneMorte = estVerte ? 18.0 : 16.0; // zone morte
|
2026-03-28 14:18:17 +01:00
|
|
|
double vitesseVerticaleMax = estVerte ? 4.2 : 2.6; // vitesses verticales plus hautes
|
2026-03-28 17:19:34 +01:00
|
|
|
double proximiteJoueur = clamp((540.0 - x) / 280.0, 0.0, 1.0);
|
|
|
|
|
|
|
|
|
|
if (!estVerte) {
|
|
|
|
|
// Plus rouge proche du joueur, plus on ouvre la fenêtre d'esquive.
|
|
|
|
|
vitessePoursuite *= (1.0 - proximiteJoueur * 0.66);
|
|
|
|
|
limiteVerticale *= (1.0 - proximiteJoueur * 0.52);
|
|
|
|
|
vitesseVerticaleMax *= (1.0 - proximiteJoueur * 0.40);
|
|
|
|
|
zoneMorte += proximiteJoueur * 22.0;
|
|
|
|
|
}
|
2026-03-28 14:18:17 +01:00
|
|
|
|
|
|
|
|
double vitesseLigneSecurisee = Math.max(0.0, vitesseLigne);
|
|
|
|
|
double vitesseCible = Math.min(vitesseMax, vitesseLigneSecurisee * facteurHorizontal);
|
|
|
|
|
if (!estVerte) {
|
|
|
|
|
vitesseCible = vitesseCible * 0.68 + 4.5 * 0.32;
|
|
|
|
|
}
|
|
|
|
|
double deltaVitesse = clamp(vitesseCible - vitesseHorizontaleLisse, -0.25, 0.25);
|
|
|
|
|
double vitesseMaxLocale = vitesseMax;
|
|
|
|
|
if (!estVerte && x < 380) {
|
|
|
|
|
double facteurApproche = clamp((380.0 - x) / 180.0, 0.0, 1.0);
|
|
|
|
|
vitesseMaxLocale = vitesseMax - (0.55 * facteurApproche);
|
|
|
|
|
}
|
|
|
|
|
vitesseHorizontaleLisse = clamp(vitesseHorizontaleLisse + deltaVitesse, 4.0, vitesseMaxLocale);
|
|
|
|
|
vitesse = vitesseHorizontaleLisse;
|
|
|
|
|
x -= vitesse;
|
|
|
|
|
|
|
|
|
|
double vitesseJoueurLimitee = clamp(cibleVitesseY, -6.0, 6.0);
|
|
|
|
|
double anticipation = estVerte ? vitesseJoueurLimitee * 3.2 : 0.0;
|
|
|
|
|
double cibleLisse = cibleY + anticipation + (estVerte ? Math.sin(frameCounter * 0.055 + phaseOndulation) * 45.0 : 0.0);
|
|
|
|
|
|
|
|
|
|
double deltaY = cibleLisse - y;
|
2026-03-28 17:19:34 +01:00
|
|
|
double ratioEcartY = clamp(Math.abs(deltaY) / 120.0, 0.50, 1.60);
|
|
|
|
|
vitessePoursuite *= ratioEcartY;
|
|
|
|
|
limiteVerticale *= Math.sqrt(ratioEcartY);
|
|
|
|
|
|
2026-03-28 14:18:17 +01:00
|
|
|
if (Math.abs(deltaY) < zoneMorte) {
|
|
|
|
|
deltaY = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vitesseVerticale += clamp(deltaY * vitessePoursuite, -limiteVerticale, limiteVerticale);
|
|
|
|
|
vitesseVerticale *= amortissement;
|
|
|
|
|
vitesseVerticale = clamp(vitesseVerticale, -vitesseVerticaleMax, vitesseVerticaleMax);
|
|
|
|
|
|
|
|
|
|
double ondulation = Math.sin(frameCounter * 0.085 + (estVerte ? 0.0 : 1.1)) * amplitudeOndulation;
|
|
|
|
|
y += vitesseVerticale + ondulation;
|
|
|
|
|
frameCounter++;
|
|
|
|
|
|
|
|
|
|
if (y < 20) {
|
|
|
|
|
y = 20;
|
|
|
|
|
vitesseVerticale = 0;
|
|
|
|
|
} else if (y > 580) {
|
|
|
|
|
y = 580;
|
|
|
|
|
vitesseVerticale = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-28 08:16:44 +01:00
|
|
|
|
|
|
|
|
// Vérifier collision avec le cercle
|
|
|
|
|
public boolean collisionAvec(Cercle c) {
|
|
|
|
|
double cx = c.getX();
|
|
|
|
|
double cy = c.getY();
|
|
|
|
|
double cRayon = c.getRayon();
|
|
|
|
|
|
|
|
|
|
double dist = Math.hypot(cx - x, cy - y);
|
2026-03-28 17:19:34 +01:00
|
|
|
double seuil = estVerte ? (rayon + cRayon - 6.0) : (rayon + cRayon - 10.5);
|
2026-03-28 14:18:17 +01:00
|
|
|
return dist <= seuil;
|
2026-03-28 08:16:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setVitesse(double vitesse) {
|
|
|
|
|
this.vitesse = vitesse;
|
|
|
|
|
}
|
|
|
|
|
}
|