Files
archived-L3-racing-game/src/Game.java

310 lines
7.9 KiB
Java

import java.awt.Color;
import java.util.ArrayList;
/**
* La classe {@link Game} représente le moteur principal du jeu.
* <p>
* Elle contient le modèle (les voitures, l'état du jeu, la carte),
* la vue (Track, Dashboard, Rankboard) et la logique de contrôle
* (pause, boucle de jeu, observateurs).
* </p>
*/
public class Game
{
/**
* {@link CarInfo} est une structure simple qui contient les informations
* nécessaires pour créer une voiture dans le jeu : son nom et sa couleur.
*/
public static class CarInfo
{
/** Nom de la voiture */
public final String name;
/** Couleur de la voiture */
public final Color color;
public CarInfo(String name, Color color, boolean visual)
{
this.name = name;
this.color = color;
}
}
/**
* Builder pour créer une instance de {@link Game} de façon fluide.
* <p>
* Permet d'ajouter des voitures, de définir la carte, l'état initial et
* le temps entre les étapes du jeu avant de construire l'objet final.
* </p>
*/
public static class Builder
{
/** Liste des voitures à créer pour le jeu */
private ArrayList<CarInfo> cars = new ArrayList<>();
/** État initial du jeu */
private State state = new State();
/** Temps entre chaque step du jeu */
private int time = 1000;
/** Carte sur laquelle se déroule le jeu */
private Map map = null;
/** Ajoute une voiture à la liste */
public Builder addCar(CarInfo car)
{
cars.add(car);
return this;
}
/** Supprime une voiture de la liste */
public Builder remCar(CarInfo car)
{
cars.remove(car);
return this;
}
/** Définit la carte du jeu */
public Builder setMap(Map map)
{
this.map = map;
return this;
}
/** Définit l'état initial du jeu */
public Builder setState(State s)
{
this.state = s;
return this;
}
/** Définit le temps entre chaque étape du jeu */
public Builder setTime(int time)
{
this.time = time;
return this;
}
/**
* Construit l'instance de {@link Game} avec les paramètres définis.
* <p>
* Si la carte n'a pas été définie, le programme s'arrête.
* </p>
*/
public Game build()
{
if (map == null)
{
System.err.println("Vous devez définir une carte avant de construire le jeu !");
System.exit(1);
}
return new Game(map, cars, state, time);
}
}
private final Map map;
/** État du jeu (par exemple, positions, carburant) */
private final State state;
/** Temps entre chaque étape du jeu en millisecondes */
private final int time;
/** Liste des voitures du jeu */
private final ArrayList<Car> cars = new ArrayList<>();
/** Liste des observateurs pour la mise à jour des vues */
private final ArrayList<GObserver> obs = new ArrayList<>();
/** Indique si le jeu est en pause */
private boolean isPaused = false;
/**
* Constructeur principal.
*
* @param map carte du jeu
* @param carInfos liste des informations des voitures à créer
* @param state état initial du jeu
* @param time temps entre chaque étape du jeu
*/
public Game(Map map, ArrayList<CarInfo> carInfos, State state, int time)
{
this.map = map;
this.state = state;
this.time = time;
init(carInfos);
}
/**
* Initialise le jeu en créant les vues et les objets {@link Car}.
* <p>
* Chaque voiture obtient un Dashboard, la piste est affichée avec Track,
* et le Rankboard est créé pour afficher le classement.
* </p>
*
* @param carInfos liste des informations des voitures
* @return l'instance de Game
*/
private Game init(ArrayList<CarInfo> carInfos)
{
final int loop = map.getPathSize();
// Création de chaque voiture avec son Dashboard
for (CarInfo ci : carInfos)
cars.add(new Car(ci.name, ci.color, loop, state));
return this;
}
/**
* Vérifie si le jeu est terminé.
* <p>
* Le jeu est fini si au moins une voiture a épuisé son carburant.
* </p>
*
* @return true si le jeu est terminé
*/
private boolean isFinish()
{
for (Car car : cars)
{
if (car.getFuel() == 0)
return true;
}
return false;
}
/**
* Ajoute un observateur pour recevoir les mises à jour du jeu.
*
* @param o observateur
* @return instance de Game pour chaîner
*/
public Game addObserver(GObserver o)
{
obs.add(o);
return this;
}
/**
* Supprime un observateur.
*
* @param o observateur
* @return instance de Game pour chaîner
*/
public Game remObserver(GObserver o)
{
obs.remove(o);
return this;
}
/**
* Bascule l'état de pause du jeu.
* <p>
* Si le jeu était en pause, il est relancé et les threads en attente sont notifiés.
* </p>
*
* @return true si le jeu est maintenant en pause
*/
public synchronized boolean togglePause()
{
if (isPaused) notifyAll();
isPaused = !isPaused;
return isPaused;
}
public void notifyObservers()
{
for (GObserver o : obs)
{
boolean isSuccess = o.apply();
if (!isSuccess)
{
System.err.println("Une erreur s'est produite pendant le jeu.");
System.exit(1);
}
}
}
/**
* Exécute un cycle du jeu
* <p>
* Chaque voiture effectue son action, puis les observateurs sont notifiés.
* La boucle attend si le jeu est en pause.
* </p>
*
* @throws InterruptedException si le thread est interrompu pendant wait()
*/
private void step() throws InterruptedException
{
for (Car car : cars)
{
synchronized(this)
{
while (isPaused)
wait();
}
car.run();
notifyObservers();
}
}
/**
* Boucle principale du jeu.
* <p>
* Tant que le jeu n'est pas terminé, on exécute les étapes et on met à jour
* les vues toutes les 'time' millisecondes.
* </p>
*/
public void run()
{
while (!isFinish())
{
try
{
step();
Thread.sleep(time);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
public ArrayList<Car> getCars()
{
return cars;
}
public Map getMap()
{
return map;
}
public State getState()
{
return state;
}
public boolean getPause()
{
return isPaused;
}
/** Carte sur laquelle le jeu se déroule */
public Game addVisual(Class<? extends GameView> c, String title, int width, int height, int x, int y)
{
if (c == Rankboard.class)
obs.add(new Rankboard(this, title, width, height, x, y));
else if (c == Track.class)
obs.add(new Track(this, title, width, height, x, y));
else if (c == Dashboard.class)
{
for (Car car : cars)
if (car.getName().equals(title))
{
obs.add(new Dashboard(this, car, title, width, height, x, y));
break;
}
}
return this;
}
}