package oop13.space.game;
import java.util.List;
import javax.swing.JOptionPane;
import oop13.space.model.Alien;
import oop13.space.model.AlienBlock;
import oop13.space.model.AlienMotherShip;
import oop13.space.model.AlienShot;
import oop13.space.model.IModel;
import oop13.space.model.Individual;
import oop13.space.model.Ship;
import oop13.space.utilities.AudioPlayer;
import oop13.space.utilities.GameStrings;
import oop13.space.views.GamePanel.GamePanelObserver;
/**
* This class is a Thread that controls the game run-time routine and manages
* his event (such as Alien shot, mothership , removing dead Individuals, ...)<p>
*
* Also manages the level-related information and checks when
* a level is completed or the game is over. <p>
*
* Game statistics (number of aliens killed, Level completed, ...)
* are also calculated here.
*
* @author Mattia Capucci
* @author Manuel Bottazzi
*/
public class Game extends Thread {
private static final int SLEEP_TIME = 15;
private static final int ALIENS_SLEEP_TIME = 20;
private static final int KILLED_ALIENS_BEFORE_SPEEDUP = 54;
private static final int MOTHERSHIP_BASE_FREQ = 600;
private static final int MOTHERSHIP_SPAWN_FREQ = 1850;
private static final int SHOT_STEP = 10;
private static final int SHOT_RATIO = 100;
// Lower this value is, higher the frequency of firing will be.
private IModel model;
private GamePanelObserver obs;
private volatile boolean isOver;
private int aliensKilled;
private int levelsCompleted;
private int motherShipsKilled;
/**
* Create a new Game on the given model.
*
* @param model - The data model used by the class.
* @param obs - The Observer of the current Game.
*/
public Game(IModel model, GamePanelObserver obs) {
this.model = model;
this.obs = obs;
}
/**
* When invoked stops every continuous sound
* reproduced in the game.
*/
private void stopSounds() {
AudioPlayer.getAudioPlayer().stopAll();
}
/**
* Chooses randomly an alien that can shoot
* and adds a new alien shot.
*/
private void alienShot() {
Alien a = model.getAliens().getShooter();
if (a != null) {
AlienShot shot = a.fire();
model.addAlienShot(shot);
shot.moveIndividual(a.getPositionX() + SHOT_STEP, a.getPositionY());
}
}
@Override
public void run() {
int counterTimer = 0;
int lives = model.getShip().getLives();
double motherSpawnMoment = Math.random() * MOTHERSHIP_SPAWN_FREQ;
AlienBlock alienBlock = model.getAliens();
Ship spaceShip = model.getShip();
List<Individual> individuals = model.getList();
alienBlock.startMove();
AudioPlayer.getAudioPlayer().playLoop(AudioPlayer.BACKGROUND_SOUND);
while (!this.isOver) {
counterTimer++;
if (counterTimer % SHOT_RATIO == 0) {
alienShot();
}
//Adds a mother ship to the game
if (counterTimer >= motherSpawnMoment) {
AlienMotherShip motherShip = model.addMotherShip();
motherShip.startMove();
motherSpawnMoment = MOTHERSHIP_BASE_FREQ + Math.random() * MOTHERSHIP_SPAWN_FREQ;
counterTimer = 0;
}
/* Moves all the individuals
* (except the aliens 'cause they already move).
* Checks all the possible collisions of all individuals
* Checks if some aliens are dead
* Sets lives and score in the game panel */
for (Individual ind : individuals) {
if (!(ind instanceof Alien)) {
ind.moveIndividual(ind.getPositionX(), ind.getPositionY());
}
ind.collideWith(individuals);
if (spaceShip.getLives() != lives) {
this.obs.setShipLivesInPanel();
lives--;
}
if (ind.isDead()) {
this.model.removeIndividual(ind);
if (ind instanceof Alien && !(ind instanceof AlienMotherShip)) {
AudioPlayer.getAudioPlayer().playOnce(AudioPlayer.ALIEN_KILLED_SOUND);
this.aliensKilled++;
}
if (ind instanceof AlienMotherShip) {
if (!((AlienMotherShip) ind).isOutOfLimit()) {
this.motherShipsKilled++;
}
}
this.obs.setScoreInPanel(this.model.getStatistics().getScore());
}
}
//Speed up the last alien survivor
if (this.aliensKilled >= KILLED_ALIENS_BEFORE_SPEEDUP) {
alienBlock.setSleepTime(ALIENS_SLEEP_TIME);
}
//Check game won
if (alienBlock.checkGameWon()) {
this.levelsCompleted++;
this.obs.gameWon();
this.isOver = true;
}
//Check game over
if (alienBlock.checkGameOver() || spaceShip.isDead()) {
//Stops the alien block's thread
if (!alienBlock.checkGameOver()) {
alienBlock.setGameOver();
}
spaceShip.setLives(0);
this.obs.gameOver();
this.isOver = true;
}
this.obs.updatePanel();
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {
JOptionPane.showMessageDialog(null, GameStrings.THREAD_INTERRUPTED_ERROR, GameStrings.ERROR, JOptionPane.ERROR_MESSAGE);
}
}
this.stopSounds();
this.model.getStatistics().setAllStatistics(this.aliensKilled, this.motherShipsKilled, this.levelsCompleted);
this.obs.checkAchievements();
}
}