/*
	Rogue Trader.
	Copyright (c) 2007, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################
//
//
// Class Name:   Game
// Purpose:      Central game class. Runs the window and does a high level game
//               loop. The game class is also an event listener that can receive
//               events from the window.
//
//
//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

#include "Game.h"

//------------------------------------------------------------------------------
// Constant initialisation
//------------------------------------------------------------------------------

const float Game::REREFERNCE_RESOLUTION_X = 800;		// Resolution the game was originally 'designed' for. We scale the HUD for any resolutions other than this.
const float Game::REREFERNCE_RESOLUTION_Y = 600;		// Resolution the game was originally 'designed' for. We scale the HUD for any resolutions other than this.

const unsigned  Game::FRAME_TIME_UPDATE_FRAMES          = 8;        // Number of frames over which the current frame rate is determined
const unsigned  Game::GALAXY_SEED                       = 34433;    // seed used for the randomnly generated galaxy
const float     Game::GOOD_VALUE_DANGER_LEVEL           = 6000.0f;  // when the players goods are at this value, the danger level from having goods on board is at its highest
const float		Game::BASE_GOOD_VALUE_DANGER_LEVEL		= 500.0f;	// amount added to the goods danger level, regardless of what the player actually has
const float     Game::BASE_DANGER_LEVEL                 = 0.85f;    // base danger level for anywhere in the game
const float     Game::MISSILE_WARNING_SOUND_INTERVAL    = 0.7f;     // Time in between missile warning sounds

const float     Game::REPAIR_COST                       = 0.60f;    // repair cost per unit of damage
const float     Game::MISSILE_COST                      = 100.0f;   // cost for a missile
const float     Game::EMP_COST                          = 50.0f;    // cost for an emp bomb

const float     Game::AIPLAYER_START_DISTANCE       = 16000.0f;		// Distance from the space station the AI ships start at

const float     Game::DEATH_CAMERA_TIME             = 3.0f;			// amount of time the death camera hovers around player ship when player has died
const float     Game::DEATH_CAMERA_DISTANCE         = 800.0f;		// distance from the players ship the death camera is
const float     Game::DEATH_CAMERA_ROTATE_SPEED     = 1.0f;			// how fast the death camera rotates around the players ship

const float     Game::PLAYER_SPACE_START_DISTANCE   = 32000.0f;		// Distance from the space station the player will start at when coming out of hyperspace
const float     Game::PLAYER_DOCK_START_POS_X       = 0.0f;			// Player starting position for coming out of spacestation: x
const float     Game::PLAYER_DOCK_START_POS_Y       = 0.0f;			// Player starting position for coming out of spacestation: y
const float     Game::PLAYER_DOCK_START_POS_Z       = 4000.0f;		// Player starting position for coming out of spacestation: z
const double    Game::PLAYER_START_CREDITS          = 500.00;		// Amount of credits the player starts with

const float     Game::HYPERSPACE_TIME               = 1.5f;			// Time spent in the hyperspace interlude in seconds
const float     Game::HYPERSPACE_CYLINDER_RADIUS    = 4.0f;			// Radius of the hyperspace cylinder
const float     Game::HYPERSPACE_CYLINDER_SLICES    = 8.0f;			// Number of slices in the hyperspace cylinder
const float     Game::HYPERSPACE_CYLINDER_LENGHT    = 100000.0f;	// Lenght of the hyperspace cylinder
const float     Game::HYPERSPACE_TRAVEL_DISTANCE    = 200.0f;		// How far the player travels through the hyperspace cylinder
const float     Game::HYPERSPACE_TEXTURE_SCALE      = 0.0625f;		// The amount the texture coordinates of the hyperspace cylinder are scaled
const float     Game::HYPERSPACE_TEXTURE_STRETCH    = 2000.0f;		// The amount to stretch the hyperspace texture over time
const float     Game::HYPERSPACE_LAYER1_TURN_SPEED  = 8.0f;			// Speed at which the texture of layer1 rotates
const float     Game::HYPERSPACE_LAYER2_TURN_SPEED  = 12.0f;		// Speed at which the texture of layer2 rotates

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::Game()
Purpose:  Constructor. Initialises the game.
Params:   The name of the human player in the game:
Returns:  n/a

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

Game::Game( const char * playerName )
{
    //--------------------------------------------------------------------------
    // Create the games timer and reset all timing variables
    //--------------------------------------------------------------------------

    frameTimer = new Timer();

    frameTime           = 0;
    frameTimesAccum     = 0;
    frameTimesCounted   = 0;

    //--------------------------------------------------------------------------
    // Initialise the random seed and select some music to play
    //--------------------------------------------------------------------------

    mlRandomSeed( frameTimer->getTimeStamp() );  MusicSystem::playMusic();

    //--------------------------------------------------------------------------
    // Set the 3 gui buttons as not being selected last time we checked
    //--------------------------------------------------------------------------

    menuButtonSelected      = false;
    missileButtonSelected   = false;
    empButtonSelected       = false;

    //--------------------------------------------------------------------------
    // Create the game galaxy:
    //--------------------------------------------------------------------------

    galaxy = new Galaxy(GALAXY_SEED);

    //--------------------------------------------------------------------------
    // Pick a random starting system for the player:
    //--------------------------------------------------------------------------

    system = new StarSystem( galaxy->getStartSystem() );

    //--------------------------------------------------------------------------
    // Create the game scene:
    //--------------------------------------------------------------------------

    // Seed system by the background seed it has:

    mlRandomSeed( system->getBackgroundSeed() );

    // Get the sun and planet light colors from the star system:

    float starColor[3];             system->getStarColor(starColor);
    float planetLightColor[3];      system->getPlanetLightColor(planetLightColor);

    // Create the scene for the game:

    scene = new GameScene
    (
        starColor,
        planetLightColor,
        system->getPlanetTexture()
    );

    //--------------------------------------------------------------------------
    // Create the players ship and the player itself
    //--------------------------------------------------------------------------

    // Create the player ship: starts off with an explorer mark I

    Ship_ExplorerMark1 * playerShip = new Ship_ExplorerMark1(0,0,0);

    // Create a hook to the ship:

    hookPlayerShip = new GameObjectHook( playerShip );

    // Add both the hook and the ship into the scene:

    scene->addObject(playerShip);
    scene->addHook(hookPlayerShip);

    // Create the player itself

    player = new PlayerHuman( playerName, hookPlayerShip , PLAYER_START_CREDITS , this );

    // Set the amount of time the player has been dead for to zero seconds

    timePlayerDead = 0;

    //--------------------------------------------------------------------------
    // Create the spacestation and add into the scene
    //--------------------------------------------------------------------------

    // Create the spacestation

    GameObject_SpaceStation * spaceStation = new GameObject_SpaceStation
    (
        0,          // position: x
        0,          // position: y
        0,          // position: z
        this        // Game the object belongs to
    );

    // Add the spacestation into the scene

    scene->addObject(spaceStation);

    // Create a hook to the spacestation and add it into the scene:

    hookSpaceStation = new GameObjectHook(spaceStation); scene->addHook(hookSpaceStation);

    //--------------------------------------------------------------------------
    // Set player position to start position for space:
    //--------------------------------------------------------------------------

    setPlayerPosition_spaceStart();

    //--------------------------------------------------------------------------
    // Set the number of AI players to zero initially
    //--------------------------------------------------------------------------

    numAIPlayers = 0;

    //--------------------------------------------------------------------------
    // Create the death camera which hovers aroun the player for a while after
    // the player dies:
    //--------------------------------------------------------------------------

    // Make the camera

    deathCamera = new Camera();

    // Set its current angle of rotation about the x and y axes as 0

    deathCameraAngle = 0;

    //--------------------------------------------------------------------------
    // Set the time to the next missile warning sound to 0
    //--------------------------------------------------------------------------

    timeToMissileWarningSound = 0;

    //--------------------------------------------------------------------------
    // Finally pause the game before running:
    //--------------------------------------------------------------------------

    pause();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::~Game()
Purpose:  Destructor. Free up game resources.
Params:   n/a
Returns:  n/a

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

Game::~Game()
{
    delete frameTimer;			frameTimer			= 0;
    delete scene;				scene				= 0;
    delete hookPlayerShip;		hookPlayerShip		= 0;
    delete hookSpaceStation;	hookSpaceStation	= 0;
    delete player;				player				= 0;
    delete galaxy;				galaxy				= 0;
    delete system;				system				= 0;
    delete deathCamera;			deathCamera			= 0;
	
    // Delete all ai players and their ship hooks

    for ( unsigned i = 0 ; i < numAIPlayers ; i++ )
    {
        delete aiPlayers[i];		aiPlayers[i]	= 0;
        delete aiShipHooks[i];		aiShipHooks[i]	= 0;
    }

    // Stop playing music:

    MusicSystem::stopMusic();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::pause()
Purpose:  Pauses the game.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::pause()
{
    // Pause all sounds in the game scene

    scene->pauseAllSounds();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::unpause()
Purpose:  Unpauses the game.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::unpause()
{
    // Restart the frame timer:

    frameTimer->restart();

    // Reset all timing variables:

    frameTime           = 0;
    frameTimesAccum     = 0;
    frameTimesCounted   = 0;

    // Resume all paused sounds in the game scene

    scene->resumeAllSounds();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::setPlayerPosition_spaceStart()
Purpose:  Sets the players ship position to where it should be when coming out
          of hyperspace or starting the game.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::setPlayerPosition_spaceStart()
{
    // Decide on a random angle on the xz plane around the spacestation will start at:

    float startAngle = PI2 * mlRandomFloat();

    // Tell the game scene to update the players ship position:

    scene->setObjectPosition
    (
        hookPlayerShip,
        cosf( startAngle ) * PLAYER_SPACE_START_DISTANCE,
        0,
        sinf( startAngle ) * PLAYER_SPACE_START_DISTANCE
    );

    // Get the players ship object:

    GameObject_Ship * ship = (GameObject_Ship*)hookPlayerShip->getObject();

    // If null then abort:

    if ( ship == 0 ) return;

    // Reset ship orientation:

    ship->resetOrientation();

    // Rotate to face the spacestation

    ship->rotateY(PI);

    // Reset ship velocity and max velocity (target speed):

    ship->resetVelocity();
    ship->setMaxVelocity(0);

    // Get the space station object:

    GameObject * spaceStation = hookSpaceStation->getObject();

    // Make the players ship face the station:

    ship->faceObject(spaceStation);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::setPlayerPosition_dockStart()
Purpose:  Sets the players ship position to where it should be when coming out
          of docking in a spacestation.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::setPlayerPosition_dockStart()
{
    // Tell the game scene to update the players ship position:

    scene->setObjectPosition
    (
        hookPlayerShip,
        PLAYER_DOCK_START_POS_X,
        PLAYER_DOCK_START_POS_Y,
        PLAYER_DOCK_START_POS_Z
    );

    // Get the players ship object:

    GameObject_Ship * ship = (GameObject_Ship*)( hookPlayerShip->getObject() );

    // If null then abort:

    if ( ship == 0 ) return;

    // Reset ship orientation:

    ship->resetOrientation();

    // Reset ship velocity and max velocity (target speed):

    ship->resetVelocity();
    ship->setMaxVelocity(0);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::run()
Purpose:  Begins running the game. Does not return until the game is finished or
          the window is closed.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::run()
{
    //--------------------------------------------------------------------------
    // Set the run flag to true
    //--------------------------------------------------------------------------

    continueRunning = true;

    //--------------------------------------------------------------------------
    // Save the previous window event listener
    //--------------------------------------------------------------------------

    GLWindowEventListener * previousEventListener = GLWindow::getEventListener();

    //--------------------------------------------------------------------------
    // Set the game object as the current window event listener
    //--------------------------------------------------------------------------

    GLWindow::setEventListener(this);

    //--------------------------------------------------------------------------
    // Before running the game reset the frame timer
    //--------------------------------------------------------------------------

    frameTimer->restart();

    //--------------------------------------------------------------------------
    // Unpause the game before running:
    //--------------------------------------------------------------------------

    unpause();

    //--------------------------------------------------------------------------
    // Keep running the window until it is closed or the game is over: the main
    // game loop will be run in the windowRedrawing method when it is called.
    //--------------------------------------------------------------------------

    while ( continueRunning && GLWindow::exists() )
    {
        GLWindow::requestRepaint();  // request window repaint
        GLWindow::run();             // run the window: do message processing
    }

    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------

    // Restore the previous window event listener

    GLWindow::setEventListener(previousEventListener);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::stop()
Purpose:  Stops running the game.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::stop(){ continueRunning = false; }

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::windowRedrawing()
Purpose:  Redraw event for the window. This is where the main game loop happens.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::windowRedrawing()
{
    //--------------------------------------------------------------------------
    // Bug fix: sometimes mouse seems to get restricted for no reason. make sure
    // the mouse is always unrestricted:
    //--------------------------------------------------------------------------

    GLWindow::unRestrictMouse();

    //--------------------------------------------------------------------------
    // Set the random number generation seed to the value of the timer timestamp
    //--------------------------------------------------------------------------

    // mlRandomSeed( frameTimer->getTimeStamp() );
    
    mlRandomSeed( 643463 );

    //--------------------------------------------------------------------------
    // Update the current frame rate if it is time:
    //--------------------------------------------------------------------------

    // Update frame timer first

    frameTimer->update();

    // Add frame time to accumalated frame times

    frameTimesAccum += frameTimer->getSeconds(); frameTimesCounted++;

    // See if it is time to update the current frame rate amount:

    if ( frameTimesCounted >= FRAME_TIME_UPDATE_FRAMES )
    {
        // Average the accumalted frame times:

        frameTimesAccum /= float(FRAME_TIME_UPDATE_FRAMES);

        // Save as current frame time:

        frameTime = frameTimesAccum;

        // Reset everything else:

        frameTimesAccum     = 0;
        frameTimesCounted   = 0;
    }
    
    //--------------------------------------------------------------------------
    // Get the camera to view the world from
    //--------------------------------------------------------------------------

    // Store the camera here

    Camera * camera;

    // If player is alive then use the players camera, otherwise the death camera:

    if ( hookPlayerShip->getObject() )
    {
        // Player alive. Use the players camera

        camera = player->getCamera();
    }
    else
    {
        // Player dead: use the death camera

        camera = deathCamera;

        // Reset it first

        deathCamera->reset();

        // Decide on the cameras position from its current rotation angle about y axis:

        float cameraPos[3] = { 0, 0, DEATH_CAMERA_DISTANCE };

        mlRotate3dY( cameraPos , deathCameraAngle );

        // Translate the death camera by the rotated the death camera distance:

        deathCamera->move( cameraPos[0] , cameraPos[1] , cameraPos[2] );

        // Now translate it to the last known position of the ship:

        deathCamera->move( lastPlayerX , lastPlayerY , lastPlayerZ );

        // Rotate the camera orientation about the y axis by the the death camera rotation

        deathCamera->rotateY( -deathCameraAngle );

        // Roate the camera a little more:

        deathCameraAngle += DEATH_CAMERA_ROTATE_SPEED * frameTime;
    }

    //--------------------------------------------------------------------------
    // World simulation
    //--------------------------------------------------------------------------

    scene->sceneSimulation( frameTime );

    // If the players ship still exists then update the last known position of the player ship

    if ( hookPlayerShip->getObject() )
    {
        GameObject * ship = hookPlayerShip->getObject();

        lastPlayerX = ship->getPosX();
        lastPlayerY = ship->getPosY();
        lastPlayerZ = ship->getPosZ();
    }

    //--------------------------------------------------------------------------
    // AI decision making
    //--------------------------------------------------------------------------

    // First see if the AI players have lost their ships, if so then delete:

    for ( unsigned i = 0 ; i < numAIPlayers ; i++ )
    {
        // Get this players ship hook:

        GameObjectHook * hook = aiShipHooks[i];

        // Get this players ship

        GameObject * ship = hook->getObject();

        // See if this player has no ship:

        if ( ship == 0 )
        {
            // AI players ship is gone: remove the ship hook from the scene

            scene->removeHook(hook); delete hook;

            // Delete the ai player:

            delete aiPlayers[i];

            // Decrement the number of AI players:

            numAIPlayers--;

            // Swap the last AI player in the array into this AI Players position:

            aiPlayers[i]    = aiPlayers[numAIPlayers];
            aiShipHooks[i]  = aiShipHooks[numAIPlayers];

            // Increase the players kills by one:

            player->setNumKills( player->getNumKills() + 1 );
        }
    }

    // Do moves for all AI players:

    for ( unsigned i = 0 ; i < numAIPlayers ; i++ )
    {
        // Make the move for this AI player

        aiPlayers[i]->makeMove( frameTime );
    }

    //--------------------------------------------------------------------------
    // Player input processing
    //--------------------------------------------------------------------------

    player->makeMove( frameTime );

    //--------------------------------------------------------------------------
    // Game drawing
    //--------------------------------------------------------------------------

    scene->sceneRender( camera );

    //--------------------------------------------------------------------------
    // Update the sounds in the scene:
    //--------------------------------------------------------------------------

    // Get the position of the camera:

    float position[3]; camera->getPosition(position);

    // Get the orienation of the camera:

    float orientation[9]; camera->getOrientationMatrix(orientation);

    // Set the position and orientation of the listener in the game scene:

    scene->setListenerPosition(position);
    scene->setListenerOrientation(orientation);

    // Update the volume and panning of all sounds in the scene and kill dead sounds:

    scene->updateSounds();

    //--------------------------------------------------------------------------
    // Finally, if the player is still alive then draw the hud: other wise
    // increment the time the player has been dead for:
    //--------------------------------------------------------------------------

    if ( hookPlayerShip->getObject() )
    {
        // Player alive: draw hud

        drawHud();
    }
    else
    {
        // Player dead: increment time dead:

        timePlayerDead += frameTime;

        // Make sure the mouse cursor is visible

        GLWindow::setCursor( GLWindow::CURSOR_ARROW );

        // Go into 2d mode:

        GLWindow::glBeginDraw2d();

        // Setup the opengl state for fading the screen:

        glDisable(GL_CULL_FACE);
        glDisable(GL_TEXTURE_2D);
        glDisable(GL_LIGHTING);
        glEnable(GL_BLEND);

        glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

        // Gradually fade out the screen

        glColor4f(0,0,0, timePlayerDead / DEATH_CAMERA_TIME );

        glBegin(GL_QUADS);

            glVertex2f(0.0f,0.0f);
            glVertex2f((float)GLWindow::getClientWidth(),0.0f);
            glVertex2f((float)GLWindow::getClientWidth(),(float)GLWindow::getClientHeight());
            glVertex2f(0.0f,(float)GLWindow::getClientHeight());

        glEnd();

        // If the time for the death sequence is up then finish the game:

        if ( timePlayerDead >= DEATH_CAMERA_TIME )
        {
            // Display a message saying the player has been killed

            GuiScreen::showMessageDialog
            (
                "You have been killed",
                "Your ship has been destroyed. Press OK to return to main menu"
            );

            // Stop the game entirely

            this->stop();
        }
    }

    //--------------------------------------------------------------------------
    // Swap the back buffer onto the front buffer to finish up drawing
    //--------------------------------------------------------------------------

    GLWindow::swapBuffers();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::windowClosing()
Purpose:  Window closing event. Closes the window.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::windowClosing()
{
    // Close up the window

    GLWindow::close();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::keyboardPressed()
Purpose:  Key pressed event for the game.
Params:   Key that was pressed.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::keyboardPressed( GLWindow::KeyboardKey key )
{
    //--------------------------------------------------------------------------
    // If the escape key was pressed then create an in-game menu and run it:
    //--------------------------------------------------------------------------

    if ( key == GLWindow::KEYBOARD_ESCAPE )
    {
        // But only do this if the player is not dead!!

        if ( hookPlayerShip->getObject() )
        {
            // Fade out the screen

            GLWindow::fadeScreen(0.5f);

            // Create an in-game menu screen object:

            GuiScreen_InGameMenu * menu = new GuiScreen_InGameMenu(this);

            // Pause the game

            pause();

            // Run the menu:

            menu->run();

            // Unpause the game

            unpause();

            // Invalidate any time passed when in the menu with the frame timer:

            frameTimer->restart();

            // Finally delete the menu:

            delete menu;
        }
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::travelToSystem()
Purpose:  Travels the player to another star system.
Params:   Sector on which the system is
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::travelToSystem( unsigned sectorX , unsigned sectorY )
{
    //--------------------------------------------------------------------------
    // First try and get the players ship:
    //--------------------------------------------------------------------------

    // Get the ship object from the hook to it: assume this is a ship

    GameObject_Ship * ship = (GameObject_Ship*)(hookPlayerShip->getObject());

    // If the ship no longer exists then abort:

    if ( ship == 0 ) return;

    //--------------------------------------------------------------------------
    // Now get the star system at the given sector
    //--------------------------------------------------------------------------

    StarSystem system = galaxy->getSystem(sectorX,sectorY);

    // If the system is a null system with no name then abort:

    if ( system.isNullSystem() ) return;

    // Otherwise tell the galaxy to remember the details of the system we are leaving

    galaxy->rememberSystem( * this->system );

    //--------------------------------------------------------------------------
    // Pause the game:
    //--------------------------------------------------------------------------

    pause();

    //--------------------------------------------------------------------------
    // Set the players fuel as having being spent:
    //--------------------------------------------------------------------------

    ship->spendFuel();

    //--------------------------------------------------------------------------
    // Everything is ok: draw the hyperspace interlude
    //--------------------------------------------------------------------------

    // First play the hyperspace sound

    SoundSystem::playSound( SoundManager::getSndHyperspace() );

    // Change the music:

    MusicSystem::playMusic();

    // Draw the hyperspace interlude

    drawHyperspaceInterlude();

    //--------------------------------------------------------------------------
    // Draw the loading screen
    //--------------------------------------------------------------------------

    drawLoadingScreen();

    //--------------------------------------------------------------------------
    // Travel to the system specified
    //--------------------------------------------------------------------------

    setSystem( system , true , true );

    //--------------------------------------------------------------------------
    // Unpause the game:
    //--------------------------------------------------------------------------

    unpause();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::drawHud()
Purpose:  Draws the heads up display for the game.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::drawHud()
{
    //--------------------------------------------------------------------------
    // Switch into 2D mode first: but save the opengl projection and
    // modelview matrix first. Assume they have been setup for the 3D scene view
    //--------------------------------------------------------------------------

    // Save the projection matrix:

    float projectionMatrix[16]; glGetFloatv( GL_PROJECTION_MATRIX , projectionMatrix );

    // Save the modelview matrix:

    float modelViewMatrix[16]; glGetFloatv( GL_MODELVIEW_MATRIX , modelViewMatrix );

    // Switch into 2d

    GLWindow::glBeginDraw2d();
    
    // Apply GUI scaling to scale up the small GUI to large resolutions:
    
    glMatrixMode(GL_PROJECTION);
    
    {
		float scale = Game::getGuiScaleFactor();
		glScalef(scale, scale, scale);
	}
	
	glMatrixMode(GL_MODELVIEW);    
	
    //--------------------------------------------------------------------------
    // Get the effective resolution of the game
    //--------------------------------------------------------------------------
    
    float effectiveResX = 0;
    float effectiveResY = 0;
    getEffectiveResolution(effectiveResX, effectiveResY);

    //--------------------------------------------------------------------------
    // Setup opengl state:
    //--------------------------------------------------------------------------

    glDisable   (GL_DEPTH_TEST );   // disables
    glDisable   (GL_LIGHTING   );
    glDisable   (GL_BLEND      );
    glEnable    (GL_TEXTURE_2D );   // enables
    glDepthMask (GL_FALSE);         // dont bother with z buffer writes

    //--------------------------------------------------------------------------
    // Get the main font for the game from the font manager:
    //--------------------------------------------------------------------------

    GLFont * font = GLFontManager::getFont_main();

    //--------------------------------------------------------------------------
    // See if the player has a currently targeted object: draw targeting image if
    // this is the case..
    //--------------------------------------------------------------------------

    if ( player->getTarget() && scene->objectExists( player->getTarget() )  )
    {
        //----------------------------------------------------------------------
        // Get the position of the player's target object and project it into 2d:
        //----------------------------------------------------------------------

        // Get the target object first:

        GameObject * target = player->getTarget();

        // Get its position as a homogenous vector:

        float position[4]; target->getPosition(position); position[3] = 1;

        // Now first transform it by the modelview matrix, then the projection matrix:

        mlTransform4d( position , modelViewMatrix   );
        mlTransform4d( position , projectionMatrix  );

        // Do the perspective divide by the w coordinate: dont bother doing for z since it is not needed

        position[0] /= position[3];
        position[1] /= position[3];

        // Now this is a normalised device coordinate, un-normalise to get an actual screen coordinate:

        position[1] *= -1;                      // flip the y coord first. y axis on screen goes downwards, not according to cartesian coords.
        position[0] +=  1;                      // bring x coordinate in 0 - 2 range
        position[1] +=  1;                      // bring y coordinate in 0 - 2 range
        position[0] *=  effectiveResX / 2.0f;	// scale coordinates to screen size
        position[1] *=  effectiveResY / 2.0f;	// scale coordinates to screen size

        // Truncate this position to integer coordinates

        position[0] = (float) int( position[0] );
        position[1] = (float) int( position[1] );

        //----------------------------------------------------------------------
        // Draw the targeting box
        //----------------------------------------------------------------------

        // Enable alpha blending:

        glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

        // Now bind the targeting texture:
	
        GLTextureManager::getTexTarget()->bind();
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

        // Make sure current color is opaque white:

        glColor4f(1,1,1,1);

        // Draw the targeting box:

        glBegin( GL_QUADS );

            glTexCoord2f(0,0);  glVertex2f( position[0] - 32 , position[1] - 32 );
            glTexCoord2f(1,0);  glVertex2f( position[0] + 32 , position[1] - 32 );
            glTexCoord2f(1,1);  glVertex2f( position[0] + 32 , position[1] + 32 );
            glTexCoord2f(0,1);  glVertex2f( position[0] - 32 , position[1] + 32 );

        glEnd();

        //----------------------------------------------------------------------
        // Draw the targets name
        //----------------------------------------------------------------------

        // Get the lenght of the targets name:

        int lenght = strlen(target->getName());

        // Draw the name of the target centered above the targeting box and in near green:

        font->drawString
        (
            target->getName(),
            float( int( position[0] - float( lenght ) * 4.0f	) ),
            float( int( position[1] - 48.0f						) ),
            8.0f,
            0.6f,
            1.0f,
            0.6f
        );

        //----------------------------------------------------------------------
        // Get the distance to the players ship and draw:
        //----------------------------------------------------------------------

        // Get the players ship first:

        GameObject * playerShip = player->getShipHook()->getObject();

        // Only do this if the players ship exists:

        if ( playerShip )
        {
            // Get the position of the players ship and target:

            float targetPos[3]; target->getPosition(targetPos);
            float playerPos[3]; playerShip->getPosition(playerPos);

            // Get the distance between these two points:

            float dist = mlDistance3d( targetPos , playerPos );

            // Interpret 1 unit as a metre, but print the amount in km to a string buffer:

            char buffer[32]; sprintf_s( buffer , 32 , "%1.2f km" , dist / 1000.0f );

            // Get the lenght of this string

            lenght = strlen(buffer);

            // Draw the distance to the target centered below the targeting box and in near green:

            font->drawString
            (
                buffer,
                float( int( position[0] - float(lenght) * 4.0f	) ),
                float( int( position[1] + 40.0f					) ),
                8.0f,
                0.6f,
                1.0f,
                0.6f
            );
        }

    }   // end if player has valid target

    //--------------------------------------------------------------------------
    // Get all the needed information for the ship guages:
    //--------------------------------------------------------------------------

    // Store ship information here:

    float shipSpeed         = 0;
    float shipTargetSpeed   = 0;
    float shipMaxSpeed      = 1;
    float shipLaserHeat     = 0;
    float shipHealth        = 0;
    float shipMaxHealth     = 1;

    // See if the players ship still exists:

    if ( hookPlayerShip->getObject() )
    {
        // Get the ship the player controls:

        GameObject_Ship * ship = ( GameObject_Ship * ) hookPlayerShip->getObject();

        // Get its current speed:

        shipSpeed = ship->getVelocityAmount();

        // Get its target speed (max velocity):

        shipTargetSpeed = ship->getMaxVelocity();

        // Get its maximum speed:

        shipMaxSpeed = ship->getMaxSpeed();

        // If current speed is above the maximum, then clamp it for visual purposes:

        if ( shipSpeed > shipMaxSpeed ) shipSpeed = shipMaxSpeed;

        // Get its laser heat level:

        shipLaserHeat = ship->getLaserHeatLevel();

        // Get its current health:

        shipHealth = ship->getHealth();

        // Get its maximum health:

        shipMaxHealth = ship->getMaxHealth();
    }

    //--------------------------------------------------------------------------
    // Draw the target and current speed display element
    //--------------------------------------------------------------------------

    // Bind the texture for this UI element:

    GLTextureManager::getTexGuageSpeeds()->bind();
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);    

    // Make sure texturing is enabled:

    glEnable(GL_TEXTURE_2D);

    // Make sure alpha blending is enabled:

    glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

    // Set the draw color

    glColor4f(1,1,1,1);

    // Draw this element:

    glBegin(GL_QUADS);

        glTexCoord2f(0,0);  glVertex2f( effectiveResX / 2.0f - 128.0f , effectiveResY - 64.0f );
        glTexCoord2f(1,0);  glVertex2f( effectiveResX / 2.0f + 128.0f , effectiveResY - 64.0f );
        glTexCoord2f(1,1);  glVertex2f( effectiveResX / 2.0f + 128.0f , effectiveResY - 32.0f );
        glTexCoord2f(0,1);  glVertex2f( effectiveResX / 2.0f - 128.0f , effectiveResY - 32.0f );

    glEnd();

    // Decide on the width of the target and current speed bars:

    float curWidth = mlRound( ( shipTargetSpeed / shipMaxSpeed ) * 128.0f );
    float tarWidth = mlRound( ( shipSpeed       / shipMaxSpeed ) * 128.0f );

    // Draw the current speed and target speed bars:

    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);

    glBegin(GL_QUADS);      // draw current speed bar

        glColor3f(0,1,0);

        glVertex2f( effectiveResX / 2.0f - 64.0f              , effectiveResY - 36.0f );
        glVertex2f( effectiveResX / 2.0f - 64.0f + curWidth   , effectiveResY - 36.0f );
        glVertex2f( effectiveResX / 2.0f - 64.0f + curWidth   , effectiveResY - 40.0f );
        glVertex2f( effectiveResX / 2.0f - 64.0f              , effectiveResY - 40.0f );

    glEnd();

    glBegin(GL_QUADS);      // draw target speed bar

        glColor3f(0,1,0);

        glVertex2f( effectiveResX / 2.0f - 64.0f              , effectiveResY - 40.0f );
        glVertex2f( effectiveResX / 2.0f - 64.0f + tarWidth   , effectiveResY - 40.0f );
        glVertex2f( effectiveResX / 2.0f - 64.0f + tarWidth   , effectiveResY - 44.0f );
        glVertex2f( effectiveResX / 2.0f - 64.0f              , effectiveResY - 44.0f );

    glEnd();

    // Get the players speed in kmph. Ship speed is in metres per second

    float kmph = ( shipSpeed / 1000.0f ) * 60 * 60;

    // Print this to a character buffer

    char buffer[32]; sprintf_s( buffer, 32 , "%1.2f kmph" , kmph);

    // Draw to the speed guage element, centered horizontally

    font->drawString
    (
        buffer												,
        float( effectiveResX / 2 - strlen(buffer) * 4	)	,
        float( effectiveResY - 58						)	,
        8.0f												,
        0.6f												,
        1.0f												,
        0.6f
    );

    //--------------------------------------------------------------------------
    // Draw the heat guage and heat reading
    //--------------------------------------------------------------------------

    // Bind the texture for this UI element:

    GLTextureManager::getTexGuageHeatHull()->bind();
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);    

    // Make sure texturing is enabled:

    glEnable(GL_TEXTURE_2D);

    // Make sure alpha blending is enabled:

    glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

    // Draw the heat guage element:

    glBegin(GL_QUADS);

        glColor4f(1,1,1,1);

        glTexCoord2f( 0, 0 );   glVertex2f( effectiveResX / 2.0f - 160.0f , effectiveResY - 64.0f	);
        glTexCoord2f( 1, 0 );   glVertex2f( effectiveResX / 2.0f - 32.0f  , effectiveResY - 64.0f	);
        glTexCoord2f( 1, 1 );   glVertex2f( effectiveResX / 2.0f - 32.0f  , effectiveResY			);
        glTexCoord2f( 0, 1 );   glVertex2f( effectiveResX / 2.0f - 160.0f , effectiveResY			);

    glEnd();

    // Figure out how high the heat bar will be depending on current heat

    float heatHeight = mlRound( ( shipLaserHeat / Laser::MAX_HEAT ) * 48.0f );

    // Draw the heat amount bar:

    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);

    glBegin(GL_QUADS);

        glColor3f(1,0,0);

        glVertex2f( effectiveResX / 2.0f - 144.0f + heatHeight, effectiveResY - 8.0f - heatHeight	);
        glVertex2f( effectiveResX / 2.0f - 128.0f + heatHeight, effectiveResY - 8.0f - heatHeight	);
        glVertex2f( effectiveResX / 2.0f - 128.0f             , effectiveResY - 8.0f				);
        glVertex2f( effectiveResX / 2.0f - 144.0f             , effectiveResY - 8.0f				);

    glEnd();

    //--------------------------------------------------------------------------
    // Draw the hull integrity guage and integrity reading
    //--------------------------------------------------------------------------

    // Bind the texture for this UI element:

    GLTextureManager::getTexGuageHeatHull()->bind();
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

    // Make sure texturing is enabled:

    glEnable(GL_TEXTURE_2D);

    // Make sure alpha blending is enabled:

    glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

    // Draw the hull integrity element:

    glBegin(GL_QUADS);

        glColor4f(1,1,1,1);

        glTexCoord2f( 0, 0 );   glVertex2f( effectiveResX / 2.0f + 160.0f , effectiveResY - 64.0f	);
        glTexCoord2f( 1, 0 );   glVertex2f( effectiveResX / 2.0f + 32.0f  , effectiveResY - 64.0f	);
        glTexCoord2f( 1, 1 );   glVertex2f( effectiveResX / 2.0f + 32.0f  , effectiveResY			);
        glTexCoord2f( 0, 1 );   glVertex2f( effectiveResX / 2.0f + 160.0f , effectiveResY			);

    glEnd();

    // Figure out how high the hull integrity bar will be depending on current ship condition

    float hullHeight = mlRound( ( shipHealth / shipMaxHealth ) * 48.0f );

    // Draw the hull integrity amount bar:

    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);

    glBegin(GL_QUADS);

        glColor3f(0,0,1);

        glVertex2f( effectiveResX / 2.0f + 144.0f - hullHeight, effectiveResY - 8.0f - hullHeight	);
        glVertex2f( effectiveResX / 2.0f + 128.0f - hullHeight, effectiveResY - 8.0f - hullHeight	);
        glVertex2f( effectiveResX / 2.0f + 128.0f             , effectiveResY - 8.0f				);
        glVertex2f( effectiveResX / 2.0f + 144.0f             , effectiveResY - 8.0f				);

    glEnd();

    //--------------------------------------------------------------------------
    // Draw the menu button:
    //--------------------------------------------------------------------------

    // Get the mouse position first:

    float mouseX = (float) GLWindow::getMouseX() / Game::getGuiScaleFactor();
    float mouseY = (float) GLWindow::getMouseY() / Game::getGuiScaleFactor();

    // Make sure texturing is enabled:

    glEnable(GL_TEXTURE_2D);

    // Enable alpha blending for this

    glEnable(GL_BLEND); glBlendFunc( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );

    // Set draw color:

    glColor4f(1,1,1,1);

    // Makeup a polygon for the button:

    float buttonMenuPolygon[] =
    {
        float( effectiveResX / 2 - 128	) ,		// point 1
        float( effectiveResY			) ,
        float( effectiveResX / 2 - 96	) ,     // point 2
        float( effectiveResY - 32		) ,
        float( effectiveResX / 2 + 96	) ,     // point 3
        float( effectiveResY - 32		) ,
        float( effectiveResX / 2 + 128	) ,		// point 4
        float( effectiveResY			)
    };

    // Now see if the mouse is within the button polygon: bind highlighted button texture if so

    if ( mlPointInPolygon2d( mouseX, mouseY, buttonMenuPolygon, 4 )  )
    {
        // If the mouse was not over the menu button last time then play a sound:

        if ( menuButtonSelected == false ) SoundSystem::playSound( SoundManager::getSndButtonOver() );

        // Bind the right texture

        GLTextureManager::getTexButtonMenuH()->bind();
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);        

        // Set the menu button as now being selected

        menuButtonSelected = true;
    }
    else
    {
        // Bind the right texture

        GLTextureManager::getTexButtonMenu()->bind();
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);        

        // Set the menu button as now not being selected

        menuButtonSelected = false;
    }

    // Set draw color to white:

    glColor4f(1,1,1,1);

    // Draw this button:

    glBegin( GL_QUADS );

        glTexCoord2f(0,0);  glVertex2f( effectiveResX / 2.0f - 128.0f , effectiveResY - 32.0f	);
        glTexCoord2f(1,0);  glVertex2f( effectiveResX / 2.0f + 128.0f , effectiveResY - 32.0f	);
        glTexCoord2f(1,1);  glVertex2f( effectiveResX / 2.0f + 128.0f , effectiveResY			);
        glTexCoord2f(0,1);  glVertex2f( effectiveResX / 2.0f - 128.0f , effectiveResY			);

    glEnd();

    //--------------------------------------------------------------------------
    // Draw the crosshair
    //--------------------------------------------------------------------------

    // Only draw it if in the 1st person mode:

    if ( player->inFirstPersonMode() )
    {
        // Get the crosshair texture:

        const GLTexture * crosshair = GLTextureManager::getTexCrosshair();

        // Bind this texture:

        crosshair->bind();
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);        

        // Get the width and height of the crosshair:

        float crossW = (float) crosshair->getOriginalSizeX();
        float crossH = (float) crosshair->getOriginalSizeY();

        // Now draw it:

        glBegin( GL_QUADS );

            glTexCoord2f(0,0);  glVertex2f( effectiveResX / 2.0f - crossW / 2.0f , effectiveResY / 2.0f - crossH / 2.0f );
            glTexCoord2f(1,0);  glVertex2f( effectiveResX / 2.0f + crossW / 2.0f , effectiveResY / 2.0f - crossH / 2.0f );
            glTexCoord2f(1,1);  glVertex2f( effectiveResX / 2.0f + crossW / 2.0f , effectiveResY / 2.0f + crossH / 2.0f );
            glTexCoord2f(0,1);  glVertex2f( effectiveResX / 2.0f - crossW / 2.0f , effectiveResY / 2.0f + crossH / 2.0f );

        glEnd();
    }

    //--------------------------------------------------------------------------
    // Draw the missile button:
    //--------------------------------------------------------------------------

    // First makeup a polygon for the missile button

    float buttonMissilePolygon[] =
    {
        float( effectiveResX / 2 - 192	),    // point 1
        float( effectiveResY			),
        float( effectiveResX / 2 - 160	),    // point 2
        float( effectiveResY - 32		),
        float( effectiveResX / 2 - 128	),    // point 3
        float( effectiveResY - 32		),
        float( effectiveResX / 2 - 160	),    // point 4
        float( effectiveResY			)
    };

    // Now see if the mouse is within the button polygon: bind highlighted button texture if so

    if ( mlPointInPolygon2d( mouseX, mouseY, buttonMissilePolygon, 4 )  )
    {
        // If the mouse was not over this button last time then play a sound:

        if ( missileButtonSelected == false ) SoundSystem::playSound( SoundManager::getSndButtonOver() );

        // Bind the right texture

        GLTextureManager::getTexButtonMissileH()->bind();
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);        

        // Set the missile button as currently selected

        missileButtonSelected = true;
    }
    else
    {
        // Bind the right texture

        GLTextureManager::getTexButtonMissile()->bind();
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);        

        // Set the missile button as not currently selected

        missileButtonSelected = false;
    }

    // Draw this button:

    glBegin( GL_QUADS );

        glTexCoord2f(0,0);  glVertex2f( effectiveResX / 2.0f - 192.0f , effectiveResY - 32.0f	);
        glTexCoord2f(1,0);  glVertex2f( effectiveResX / 2.0f - 128.0f , effectiveResY - 32.0f	);
        glTexCoord2f(1,1);  glVertex2f( effectiveResX / 2.0f - 128.0f , effectiveResY			);
        glTexCoord2f(0,1);  glVertex2f( effectiveResX / 2.0f - 192.0f , effectiveResY			);

    glEnd();

    //--------------------------------------------------------------------------
    // Draw the emp bomb button:
    //--------------------------------------------------------------------------

    // First makeup a polygon for the emp bomb button

    float buttonEmpPolygon[] =
    {
        float( effectiveResX / 2 + 128	),    // point 1
        float( effectiveResY			),
        float( effectiveResX / 2 + 160	),    // point 2
        float( effectiveResY - 32		),
        float( effectiveResX / 2 + 192	),    // point 3
        float( effectiveResY - 32		),
        float( effectiveResX / 2 + 160	),    // point 4
        float( effectiveResY			)
    };

    // Now see if the mouse is within the button polygon: bind highlighted button texture if so

    if ( mlPointInPolygon2d( mouseX, mouseY, buttonEmpPolygon, 4 )  )
    {
        // If the mouse was not over this button last time then play a sound:

        if ( empButtonSelected == false ) SoundSystem::playSound( SoundManager::getSndButtonOver() );

        // Bind the right texture

        GLTextureManager::getTexButtonEmpH()->bind();
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);        

        // Set the emp button as currently selected

        empButtonSelected = true;
    }
    else
    {
        // Bind the right texture

        GLTextureManager::getTexButtonEmp()->bind();
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);        

        // Set the emp button as not currently selected

        empButtonSelected = false;
    }

    // Draw this button:

    glBegin( GL_QUADS );

        glTexCoord2f(0,0);  glVertex2f( effectiveResX / 2.0f + 128.0f , effectiveResY - 32.0f		);
        glTexCoord2f(1,0);  glVertex2f( effectiveResX / 2.0f + 192.0f , effectiveResY - 32.0f		);
        glTexCoord2f(1,1);  glVertex2f( effectiveResX / 2.0f + 192.0f , effectiveResY				);
        glTexCoord2f(0,1);  glVertex2f( effectiveResX / 2.0f + 128.0f , effectiveResY				);

    glEnd();

    //--------------------------------------------------------------------------
    // Draw the number of missiles the players ship has
    //--------------------------------------------------------------------------

    // Only do if the players ship still exists:

    if ( hookPlayerShip->getObject() )
    {
        // Get the number of missiles the player has

        unsigned numMissiles = ((GameObject_Ship*)(hookPlayerShip->getObject()))->getNumMissiles();

        // Print this to a buffer:

        char buffer[16]; sprintf_s(buffer,16,"%1u",numMissiles);

        // Get the lenght of this string:

        unsigned lenght = strlen(buffer);

        // Draw this to the screen:

        font->drawString
        (
            buffer											,
            float( effectiveResX / 2 - 168 - lenght * 4	)	,
            float( effectiveResY - 12					)	,
            8.0f	,
            0.0f	,
            0.0f	,
            0.0f	
        );
    }

    //--------------------------------------------------------------------------
    // Draw the number of emp bombs the players ship has
    //--------------------------------------------------------------------------

    // Only do if the players ship still exists:

    if ( hookPlayerShip->getObject() )
    {
        // Get the number of missiles the player has

        unsigned numEmpBombs = ((GameObject_Ship*)(hookPlayerShip->getObject()))->getNumEmpBombs();

        // Print this to a buffer:

        char buffer[16]; sprintf_s(buffer,16,"%1u",numEmpBombs);

        // Get the lenght of this string:

        unsigned lenght = strlen(buffer);

        // Draw this to the screen:

        font->drawString
        (
            buffer,
            float( effectiveResX / 2 + 168 - lenght * 4	),
            float( effectiveResY - 12					),
            8.0f	,
            0.0f	,
            0.0f	,
            0.0f
        );
    }

    //--------------------------------------------------------------------------
    // Draw a warning message if there is an incomming missile at the player:
    //--------------------------------------------------------------------------

    GameObject * incomingMissile = findClosestIncomingMissile();

    if ( incomingMissile )
    {
        // See if we are allowed to make another missile warning sound:

        if ( timeToMissileWarningSound <= 0 )
        {
            // Allowed make a warning noise: play the noise

            SoundSystem::playSound(SoundManager::getSndMissileIncoming());

            // Set the time to the next missile warning sound

            timeToMissileWarningSound = MISSILE_WARNING_SOUND_INTERVAL;
        }
        else
        {
            // Not allowed make another missile sound. subtract frame time from time to next missile sound

            timeToMissileWarningSound -= frameTimer->getSeconds();
        }

        // Get the distance between the missile and the player:

        float distance = hookPlayerShip->getObject()->distanceTo(incomingMissile);

        // Distance is in metres. Divide by 1000 to get kilometres

        distance /= 1000;

        // Print this distance and a warning message to a string buffer:

        char buffer[256]; sprintf_s( buffer , 256 , "Warning! Incoming Missile! ( %1.2f km ) " , distance );

        // Get the big font from the font manager:

        GLFont * bigFont = GLFontManager::getFont_big();

        // Draw this message centered, to the screen in the big font:

        bigFont->drawString
        (
            buffer,
            float( effectiveResX / 2 - strlen(buffer) * 8 ),
            32.0f	,
            16.0f	,
            1.0f	,
            0.0f	,
            0.0f	
        );
    }
    else
    {
        // No incoming missile: set the time to the next missile warning sound to zero

        timeToMissileWarningSound = 0;
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::mousePressed()
Purpose:  Mouse pressed event for the game: checks to see if any of the HUD
          buttons were pressed
Params:   mouse location, mouse button pressed.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::mousePressed( int x , int y , GLWindow::MouseButton button )
{
    //--------------------------------------------------------------------------
    // If the mouse button was not the left mouse button then abort:
    //--------------------------------------------------------------------------

    if ( button != GLWindow::MOUSE_BUTTON_LEFT ) return;
    
    //--------------------------------------------------------------------------
    // Get the effective resolution of the game
    //--------------------------------------------------------------------------
    
    float effectiveResX = 0;
    float effectiveResY = 0;
    getEffectiveResolution(effectiveResX, effectiveResY);

    //--------------------------------------------------------------------------
    // Scale the mouse coordinates according to the effective res
    //--------------------------------------------------------------------------
    
    {
		float scaleFactor = getGuiScaleFactor();
		float effectiveMouseX = (float) x / scaleFactor;
		float effectiveMouseY = (float) y / scaleFactor;
		
		x = (int) effectiveMouseX;
		y = (int) effectiveMouseY;
    }

    //--------------------------------------------------------------------------
    // Get the players ship:
    //--------------------------------------------------------------------------

    GameObject_Ship * ship = (GameObject_Ship*) hookPlayerShip->getObject();

    // Abort if the ship no longer exists:

    if ( ship == 0 ) return;

    //--------------------------------------------------------------------------
    // Check to see if the menu button was pressed:
    //--------------------------------------------------------------------------

    // Makeup a polygon for the button:

    float buttonMenuPolygon[] =
    {
        float( effectiveResX / 2 - 128	),  // point 1
        float( effectiveResY			),
        float( effectiveResX / 2 - 96	),  // point 2
        float( effectiveResY - 32		),
        float( effectiveResX / 2 + 96	),  // point 3
        float( effectiveResY - 32		),
        float( effectiveResX / 2 + 128	),	// point 4
        float( effectiveResY			)
    };

    // See if the mouse point is in this polygon:

    if ( mlPointInPolygon2d( (float) x , (float) y , buttonMenuPolygon , 4 ) )
    {
        // Play the button click sound

        SoundSystem::playSound( SoundManager::getSndButtonClick() );

        // Fade out the screen

        GLWindow::fadeScreen(0.5f);

        // Point in polygon. Button was clicked. Create a new in game menu object:

        GuiScreen_InGameMenu * menu = new GuiScreen_InGameMenu(this);

        // Pause the game

        pause();

        // Run the menu:

        menu->run();

        // Unpause the game

        unpause();

        // Invalidate any time passed when in the menu with the frame timer:

        frameTimer->restart();

        // Finally delete the menu:

        delete menu;
    }

    //--------------------------------------------------------------------------
    // Check to see if the missile button was pressed
    //--------------------------------------------------------------------------

    // First makeup a polygon for the missile button

    float buttonMissilePolygon[] =
    {
        float( effectiveResX / 2 - 192	),    // point 1
        float( effectiveResY			),
        float( effectiveResX / 2 - 160	),    // point 2
        float( effectiveResY - 32		),
        float( effectiveResX / 2 - 128	),    // point 3
        float( effectiveResY - 32		),
        float( effectiveResX / 2 - 160	),    // point 4
        float( effectiveResY			)
    };

    // See if the mouse was in this when clicked

    if ( mlPointInPolygon2d( (float) x , (float) y , buttonMissilePolygon , 4 ) )
    {
        // Play the button click sound

        SoundSystem::playSound( SoundManager::getSndButtonClick() );

        // Mouse inside button: make sure the player has a target

        if ( player->getTarget() )
        {
            if ( ship->getNumMissiles() != 0 )
            {
                // Can launch a missile. Launch one.

                ship->fireMissile(player->getTarget());
            }
            else
            {
                // Pause the game:

                pause();

                // Player has no missiles. cannot launch

                GuiScreen::showMessageDialog("Cannot launch missile","Your ship is out of missiles");

                // Unpause the game:

                unpause();
            }
        }
        else
        {
            // Pause the game:

            pause();

            // Player has no target. issue a warning message:

            GuiScreen::showMessageDialog("Cannot launch missile","You do not have a current target");

            // Unpause the game:

            unpause();
        }

    }   // end if missile button clicked

    //--------------------------------------------------------------------------
    // Check to see if the emp button was pressed
    //--------------------------------------------------------------------------

    // First makeup a polygon for the emp button

    float buttonEmpPolygon[] =
    {
        float( effectiveResX / 2 + 128	),    // point 1
        float( effectiveResY - 32		),
        float( effectiveResX / 2 + 160	),    // point 2
        float( effectiveResY - 32		),
        float( effectiveResX / 2 + 192	),    // point 3
        float( effectiveResY			),
        float( effectiveResX / 2 + 160	),    // point 4
        float( effectiveResY			)
    };

    // See if the mouse was in this when clicked

    if ( mlPointInPolygon2d( (float) x , (float) y , buttonEmpPolygon , 4 ) )
    {
        // Play the button click sound

        SoundSystem::playSound( SoundManager::getSndButtonClick() );

        // See if the players ship has any emp bombs left:

        if ( ship->getNumEmpBombs() != 0 )
        {
            // Can deploy an emp bomb. do it:

            ship->deployEmp();
        }
        else
        {
            // Pause the game:

            pause();

            // No emp bombs left. issue a warning message:

            GuiScreen::showMessageDialog("Cannot deploy EMP bomb","You do not any EMP bombs left");

            // unpause the game:

            unpause();
        }

    }   // end if emp button clicked
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::drawHyperspaceInterlude()
Purpose:  Draws the hyperspace jump interlude. Basically travels the player through
          a cylinder with two rotating textures blending over each other and warping.
          Also applies some warping, blurring and fade out to add to the effect.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::drawHyperspaceInterlude()
{
    //--------------------------------------------------------------------------
    // Save the previous window event listener and disengage:
    //--------------------------------------------------------------------------

    // Save the listener:

    GLWindowEventListener * previousEventListener = GLWindow::getEventListener();

    // Disengage the window listener:

    GLWindow::setEventListener(0);

    //--------------------------------------------------------------------------
    // Save the opengl state
    //--------------------------------------------------------------------------

    // Save transform matrices

    glMatrixMode(GL_PROJECTION);    glPushMatrix();
    glMatrixMode(GL_MODELVIEW);     glPushMatrix();

    // Save enables state

    glPushAttrib( GL_ENABLE_BIT );

    //--------------------------------------------------------------------------
    // Setup the opengl state for drawing the interlude:
    //--------------------------------------------------------------------------

    // Enables / disables

    glEnable (GL_TEXTURE_2D);

    glDisable(GL_CULL_FACE);
    glDisable(GL_LIGHTING);
    glDisable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);

    glDisable(GL_CLIP_PLANE0);
    glDisable(GL_CLIP_PLANE1);
    glDisable(GL_CLIP_PLANE3);
    glDisable(GL_CLIP_PLANE4);

    //--------------------------------------------------------------------------
    // Create a timer to time the hyperpspace interlude:
    //--------------------------------------------------------------------------

    // Create timer

    Timer timer;

    // Store the time left for the interlude in seconds

    float timeLeft = HYPERSPACE_TIME;

    //--------------------------------------------------------------------------
    // Run the hyperspace interlude:
    //--------------------------------------------------------------------------

    while ( timeLeft > 0 && GLWindow::exists() )
    {
        //----------------------------------------------------------------------
        // Request a window repaint and do message processing to keep window responsive:
        //----------------------------------------------------------------------

        GLWindow::requestRepaint();
        GLWindow::run();

        // Finish all pending drawing commands:

        glFlush();

        // Clear the screen:

        glClear(GL_COLOR_BUFFER_BIT);

        // Go into 3d mode

        GLWindow::glBeginDraw3d
        (
            90,
            1,
            1000000
        );

        // Move the hyperspace cylinder by the amount the view will have travelled for current time:

        glTranslatef( 0 , 0 , ( 1.0f - timeLeft / HYPERSPACE_TIME ) * HYPERSPACE_TRAVEL_DISTANCE );

        //----------------------------------------------------------------------
        // Draw the first hyperspace cylinder layer
        //----------------------------------------------------------------------

        // Bind the hyperspace texture

        GLTextureManager::getTexHyperpspace1()->bind();

        // Mess around with the texture matrix, turn the texture:

        glMatrixMode(GL_TEXTURE);
        glLoadIdentity();
        glTranslatef(0, ( timeLeft / HYPERSPACE_TIME ) * HYPERSPACE_LAYER1_TURN_SPEED , 0 );
        glMatrixMode(GL_MODELVIEW);

        // Begin Drawing Quads

        glBegin(GL_QUADS);

        // Draw the hyperspace texture across a near infinite lenght cylinder

        for ( int i = 0 ; i < HYPERSPACE_CYLINDER_SLICES ; i ++ )
        {
            // Figure out the increment in the value passed into sinf and cosf:

            float inc = PI2 / HYPERSPACE_CYLINDER_SLICES;

            // Set draw color

            glColor4f(1,1,1,1);

            glTexCoord2f
            (
                0,
                0
            );

            glVertex3f
            (
                cosf( inc * i ) * HYPERSPACE_CYLINDER_RADIUS,
                sinf( inc * i ) * HYPERSPACE_CYLINDER_RADIUS,
                0
            );

            glTexCoord2f
            (
                HYPERSPACE_CYLINDER_LENGHT * HYPERSPACE_TEXTURE_SCALE - ( 1.0f - timeLeft / HYPERSPACE_TIME ) *  HYPERSPACE_TEXTURE_STRETCH,
                0
            );

            glVertex3f
            (
                cosf( inc * i ) * HYPERSPACE_CYLINDER_RADIUS,
                sinf( inc * i ) * HYPERSPACE_CYLINDER_RADIUS,
                -HYPERSPACE_CYLINDER_LENGHT
            );

            glTexCoord2f
            (
                HYPERSPACE_CYLINDER_LENGHT * HYPERSPACE_TEXTURE_SCALE - ( 1.0f - timeLeft / HYPERSPACE_TIME ) *  HYPERSPACE_TEXTURE_STRETCH,
                1
            );

            glVertex3f
            (
                cosf( inc * ( i + 1 ) ) * HYPERSPACE_CYLINDER_RADIUS,
                sinf( inc * ( i + 1 ) ) * HYPERSPACE_CYLINDER_RADIUS,
                -HYPERSPACE_CYLINDER_LENGHT
            );

            glTexCoord2f
            (
                0,
                1
            );

            glVertex3f
            (
                cosf( inc * ( i + 1 ) ) * HYPERSPACE_CYLINDER_RADIUS,
                sinf( inc * ( i + 1 ) ) * HYPERSPACE_CYLINDER_RADIUS,
                0
            );
        }

        // Stop drawing the hyperspace cylinder

        glEnd();

        //----------------------------------------------------------------------
        // Draw the first hyperspace cylinder layer
        //----------------------------------------------------------------------

        // Bind the hyperspace texture

        GLTextureManager::getTexHyperpspace2()->bind();

        // Enable additive blending:

        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE,GL_ONE);

        // Mess around with the texture matrix, turn the texture:

        glMatrixMode(GL_TEXTURE);
        glLoadIdentity();
        glTranslatef(0, ( timeLeft / HYPERSPACE_TIME ) * HYPERSPACE_LAYER2_TURN_SPEED , 0 );
        glMatrixMode(GL_MODELVIEW);

        // Begin Drawing Quads

        glBegin(GL_QUADS);

        // Draw the hyperspace texture across a near infinite lenght cylinder

        for ( int i = 0 ; i < HYPERSPACE_CYLINDER_SLICES ; i ++ )
        {
            // Figure out the increment in the value passed into sinf and cosf:

            float inc = PI2 / HYPERSPACE_CYLINDER_SLICES;

            // Set draw color

            glColor4f(1,1,1,1);

            glTexCoord2f
            (
                0,
                0
            );

            glVertex3f
            (
                cosf( inc * i ) * HYPERSPACE_CYLINDER_RADIUS,
                sinf( inc * i ) * HYPERSPACE_CYLINDER_RADIUS,
                0
            );

            glTexCoord2f
            (
                HYPERSPACE_CYLINDER_LENGHT * HYPERSPACE_TEXTURE_SCALE - ( 1.0f - timeLeft / HYPERSPACE_TIME ) *  HYPERSPACE_TEXTURE_STRETCH,
                0
            );

            glVertex3f
            (
                cosf( inc * i ) * HYPERSPACE_CYLINDER_RADIUS,
                sinf( inc * i ) * HYPERSPACE_CYLINDER_RADIUS,
                -HYPERSPACE_CYLINDER_LENGHT
            );

            glTexCoord2f
            (
                HYPERSPACE_CYLINDER_LENGHT * HYPERSPACE_TEXTURE_SCALE - ( 1.0f - timeLeft / HYPERSPACE_TIME ) *  HYPERSPACE_TEXTURE_STRETCH,
                1
            );

            glVertex3f
            (
                cosf( inc * ( i + 1 ) ) * HYPERSPACE_CYLINDER_RADIUS,
                sinf( inc * ( i + 1 ) ) * HYPERSPACE_CYLINDER_RADIUS,
                -HYPERSPACE_CYLINDER_LENGHT
            );

            glTexCoord2f
            (
                0,
                1
            );

            glVertex3f
            (
                cosf( inc * ( i + 1 ) ) * HYPERSPACE_CYLINDER_RADIUS,
                sinf( inc * ( i + 1 ) ) * HYPERSPACE_CYLINDER_RADIUS,
                0
            );
        }

        // Stop drawing the hyperspace cylinder

        glEnd();

        //----------------------------------------------------------------------
        // Screen blur and fade effect: increases over time
        //----------------------------------------------------------------------

        // See if this is the first frame of the effect:

        if ( timeLeft == HYPERSPACE_TIME )
        {
            // First frame: load the accumalation buffer with the back buffer:

            glAccum( GL_LOAD , 1.0f );
        }
        else
        {
            // Multiply the accumalation buffer according to time left

            glAccum( GL_MULT , 1.0f - timeLeft / HYPERSPACE_TIME );

            // Add the back buffer into the accumaltion buffer according to time left

            glAccum( GL_ACCUM , timeLeft / HYPERSPACE_TIME );

            // Copy the accumalation buffer onto the back buffer- but gradually fade

            glAccum( GL_RETURN, timeLeft / HYPERSPACE_TIME );
        }

        //----------------------------------------------------------------------
        // Finally swap the back buffer onto the screen buffer
        //----------------------------------------------------------------------

        GLWindow::swapBuffers();

        //----------------------------------------------------------------------
        // Update the timer and subtract the time passed in the last frame from
        // time left:
        //----------------------------------------------------------------------

        timer.update(); timeLeft -= timer.getSeconds();
    }

    //--------------------------------------------------------------------------
    // Restore previous opengl state
    //--------------------------------------------------------------------------

    // Restore transform matrices

    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);

    // Restore enables state

    glPopAttrib();

    //--------------------------------------------------------------------------
    // Restore the previous window event listener
    //--------------------------------------------------------------------------

    GLWindow::setEventListener(previousEventListener);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::spawnEnemies()
Purpose:  Spawns enemies around the spacestation.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::spawnEnemies()
{
    // Get the players ship

    GameObject_Ship * ship = (GameObject_Ship*)(player->getShipHook()->getObject());

    // If the players ship does not exist then abort:

    if ( ship == 0 ) return;

    // Get the danger level of the system and convert to a percentage:

    float systemDangerPercent = float( system->getDangerLevel() ) / float( StarSystem::MAX_DANGER_LEVEL );

    // Get danger level percentage based on the value of the goods the player has on board

    float goodsDangerPercent  = ( ship->getGoodsInventory()->getTotalValue() + BASE_GOOD_VALUE_DANGER_LEVEL ) / GOOD_VALUE_DANGER_LEVEL;

    // Multiply the two danger levels to get the total danger level: this will control how frequently ships are spawned:

    float dangerLevel = systemDangerPercent * goodsDangerPercent;

    // Now muliply by the base danger level for the entire game

    dangerLevel *= BASE_DANGER_LEVEL;

    // Seed the random number generator by the current clock time stamp

    mlRandomSeed( frameTimer->getTimeStamp() );

    // Now create up to the maximum number of allowed ships

    for ( unsigned i = 0 ; i < MAX_AI_SHIPS ; i++ )
    {
        // Now decide whether to spawn a ship or not:

        if ( mlRandomFloat() < dangerLevel )
        {
            // Decide on a angle on xz plane that the enemy ship will be around the spacestation

            float angle = PI2 * mlRandomFloat();

            // Randomnly decide on what ship to create: harder ships appear less often

            GameObject_Ship * ship = 0;

            switch ( mlRandomInt() % 14 )
            {
                // Explorer mark I

                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:

                    ship = new Ship_ExplorerMark1
                    (
                        cosf(angle) * AIPLAYER_START_DISTANCE,
                        0,
                        sinf(angle) * AIPLAYER_START_DISTANCE
                    );

                break;

                // Explorer mark II

                case 7:
                case 8:
                case 9:
                case 10:

                    ship = new Ship_ExplorerMark2
                    (
                        cosf(angle) * AIPLAYER_START_DISTANCE,
                        0,
                        sinf(angle) * AIPLAYER_START_DISTANCE
                    );

                break;

                // Fighter mark I

                case 11:
                case 12:

                    ship = new Ship_FighterMark1
                    (
                        cosf(angle) * AIPLAYER_START_DISTANCE,
                        0,
                        sinf(angle) * AIPLAYER_START_DISTANCE
                    );

                break;

                // Fighter mark II

                case 13:

                    ship = new Ship_FighterMark2
                    (
                        cosf(angle) * AIPLAYER_START_DISTANCE,
                        0,
                        sinf(angle) * AIPLAYER_START_DISTANCE
                    );

                break;

                // Default: should never happen

                default: break;
            }

            // Set the number of missiles this ship has randomly:

            ship->setNumMissiles( mlRandomInt() % ( ( ship->getMaxMissiles() / 2 ) + 1 ) );

            // Create a hook to this ship and save:

            aiShipHooks[numAIPlayers] = new GameObjectHook(ship);

            // Create an AI player

            aiPlayers[numAIPlayers] = new PlayerAI( aiShipHooks[numAIPlayers] , hookPlayerShip , 100 );

            // Add the ship and its hook into the game scene

            scene->addObject(ship); scene->addHook( aiShipHooks[numAIPlayers] );

            // Increment the number of AI players

            numAIPlayers++;
        }
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::findClosestIncomingMissile()
Purpose:  Finds the closest incoming misile targeted at the player
Params:   Pointer to the closest missile, null if there is none incoming
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

GameObject * Game::findClosestIncomingMissile()
{
    // Get the players ship:

    GameObject * playerShip = hookPlayerShip->getObject();

    // If the players ship no longer exists then return null

    if ( playerShip == 0 ) return 0;

    // Query the game scene for the first missile object in the scene:

    GameObject_Missile * missile = (GameObject_Missile*) scene->findObject("Missile");

    // If there is no missiles then return null:

    if ( missile == 0 ) return 0;

    //--------------------------------------------------------------------------
    // Save the first missile and its distance to the player if it is targeted
    // at the player.
    //--------------------------------------------------------------------------

    GameObject * closestMissile = 0;  float closestMissileDist = 999999999.0f;

    if ( missile->getTarget() == playerShip )
    {
        // Save as the closest missile

        closestMissile = missile;

        // Save the distance to the players ship:

        closestMissileDist = playerShip->distanceTo(missile);
    }

    //--------------------------------------------------------------------------
    // Now keep searching for more missiles and see if they are closer
    // until there is no more missiles to be checked:
    //--------------------------------------------------------------------------

	missile = (GameObject_Missile*) scene->findNextObject(missile);

    while ( missile != 0 )
    {
        // See if this missile is targeted at the player:

        if ( missile->getTarget() == playerShip )
        {
            // Missile targeted at player get distance to it and see if closer than current closest missile

            float distance = playerShip->distanceTo(missile);

            if ( distance < closestMissileDist )
            {
                // Found a closer missile targeted at the player. save.

                closestMissile      = missile;
                closestMissileDist  = distance;
            }
        }
        
        // Next missle:
        
        missile = (GameObject_Missile*) scene->findNextObject(missile);

    }   // end while there is more missiles to be checked in the game

    //--------------------------------------------------------------------------
    // Return the closest missile to the player
    //--------------------------------------------------------------------------

    return closestMissile;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::killEnemies()
Purpose:  Kills all enemies in the game.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::killEnemies()
{
    // Go through all the AI players and see if there are any ships alive:

    for ( unsigned i = 0 ; i < numAIPlayers ; i++ )
    {
        // Get the this player:

        Player * player = aiPlayers[i];

        // See if player exists:

        if ( player )
        {
            // Get player ship:

            GameObject * ship = player->getShipHook()->getObject();

            // If exists then remove from the game:

            if ( ship ) scene->deleteObject(ship);
        }
    }

    // Remove all missiles from the scene:

    GameObject * object = scene->findObject("Missile");

    while ( object != 0 )
    {
        // Found a missile: delete

        scene->deleteObject(object);
        
        // Next object:
        
        object = scene->findObject("Missile");
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::setSystem()
Purpose:  Changes the current system the player is in. Doesn't draw a hyperspace
          interlude.
Params:   System to become the current system. Whether or not to spawn enemies.
          Whether the player will start in space or near the space station
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::setSystem( const StarSystem & system , bool spawnEnemyShips , bool startInSpace )
{
    //--------------------------------------------------------------------------
    // Make sure everything is ok to make the system the current system:
    //--------------------------------------------------------------------------

    // Get the ship object from the hook to it: assume this is a ship

    GameObject_Ship * ship = (GameObject_Ship*)(hookPlayerShip->getObject());

    // If the ship no longer exists then abort:

    if ( ship == 0 ) return;

    // If the system is a null system with no name then abort:

    if ( system.isNullSystem() ) return;

    //--------------------------------------------------------------------------
    // Set the system given as the current system:
    //--------------------------------------------------------------------------

    * this->system = system;

    //--------------------------------------------------------------------------
    // Remove the player's ship and the hook to the players ship from the scene:
    //--------------------------------------------------------------------------

    // Remove the ship object:

    scene->removeObject(ship);

    // Remove the ship hook:

    scene->removeHook(hookPlayerShip);

    // Remove and delete the space station hook

    scene->removeHook(hookSpaceStation); delete hookSpaceStation;

    //--------------------------------------------------------------------------
    // Now delete the old game scene and create another:
    //--------------------------------------------------------------------------

    // Delete the game scene

    delete scene;

    // Get the sun and planet light colors from the star system:

    float starColor[3];             this->system->getStarColor(starColor);
    float planetLightColor[3];      this->system->getPlanetLightColor(planetLightColor);

    // Create the scene for the game:

    scene = new GameScene
    (
        starColor,
        planetLightColor,
        this->system->getPlanetTexture()
    );

    // Make sure all sounds in the scene are paused:

    scene->pauseAllSounds();

    //--------------------------------------------------------------------------
    // Create the spacestation and add into the scene
    //--------------------------------------------------------------------------

    // Create the spacestation

    GameObject_SpaceStation * spaceStation = new GameObject_SpaceStation
    (
        0,          // position: x
        0,          // position: y
        0,          // position: z
        this        // Game the object belongs to
    );

    // Add the spacestation into the scene

    scene->addObject(spaceStation);

    // Create a hook to this space station and add it into the scene:

    hookSpaceStation = new GameObjectHook( spaceStation ); scene->addHook(hookSpaceStation);

    //--------------------------------------------------------------------------
    // Add player ship and hook into the scene and set player start position:
    //--------------------------------------------------------------------------

    // Add both the hook and the ship into the scene:

    scene->addObject(ship);
    scene->addHook(hookPlayerShip);

    // Set player start position to either in space or outside the station:

    if ( startInSpace )
    {
        setPlayerPosition_spaceStart();
    }
    else
    {
        setPlayerPosition_dockStart();
    }

    //--------------------------------------------------------------------------
    // Delete all AI players and hooks to their ships and create a new set:
    //--------------------------------------------------------------------------

    for ( unsigned i = 0 ; i < numAIPlayers ; i++ )
    {
        delete aiPlayers[i]; delete aiShipHooks[i];
    }

    // Reset the number of ai players

    numAIPlayers = 0;

    // Spawn enemies around the space station if specified.

    if ( spawnEnemyShips ) spawnEnemies();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::drawLoadingScreen()
Purpose:  Draws a simple loading screen.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::drawLoadingScreen()
{
    // Make sure the clear color is black

    glClearColor(0,0,0,1);

    // Clear the screen:

    glClear(GL_COLOR_BUFFER_BIT);

    // Go into 2D mode:

    GLWindow::glBeginDraw2d();

    // Draw a loading message centered in the middle of the screen:

    GLFontManager::getFont_big()->drawStringCentered
    (
        "Loading.."	,
        0.0f		,
        0.0f		,
        (float) GLWindow::getClientWidth()	,
        (float) GLWindow::getClientHeight()	,
        16.0f * getGuiScaleFactor()	,
        1.0f	,
        1.0f	,
        1.0f
    );

    // Swap the back buffer onto the front buffer:

    GLWindow::swapBuffers();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::setPlayerShip()
Purpose:  Sets the current ship the player has to the ship given.
Params:   Ship to give to the player
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::setPlayerShip( GameObject_Ship * ship )
{
    // Abort if ship is null:

    if ( ship == 0 ) return;

    // If the player has a ship then remove it from the scene:

    if ( hookPlayerShip->getObject() )
    {
        // Remove the players ship from the scene and delete

        scene->removeHook(hookPlayerShip);
        scene->deleteObject(hookPlayerShip->getObject());
    }

    // Delete the ship hook:

    delete hookPlayerShip;

    // Now recreate the ship hook and make it point to the ship given:

    hookPlayerShip = new GameObjectHook(ship);

    // Set what ship the player has:

    player->setShip(hookPlayerShip);

    // Add the hook and the ship into the scene:

    scene->addObject(ship);
    scene->addHook(hookPlayerShip);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::getHudScaleFactor()
Purpose:  Gets the amount to scale up/down the GUI/HUD by in order to account 
		  for the current resolution.
Params:   none
Returns:  GUI scale factor

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

float Game::getGuiScaleFactor()
{
	// Get the original aspect ratio
	float originalA = REREFERNCE_RESOLUTION_X / REREFERNCE_RESOLUTION_Y;

	// Get the current aspect ratio
	float viewW = (float) GLWindow::getClientWidth();
    float viewH = (float) GLWindow::getClientHeight();
    
    float currentA = viewW / viewH;
    
    // See which axis to scale to scale up as much as possible whilst maintaining aspect correctness:
    if (currentA > originalA)
    {
		// Scale up y coord: will have leftover space to the left and right
		return viewH / REREFERNCE_RESOLUTION_Y;
    }
    else
    {
		// Scale up x coord: will have leftover space top and bottom
		return viewW / REREFERNCE_RESOLUTION_X;
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Game::getEffectiveResolution()
Purpose:  Returns the 'effective resolution' of the game. Which is the reference 
		  resolution scaled by the GUI scale factor, plus extra bits at the sides	
		  (or top/bottom).
Params:   Where to save the effective resolution
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void Game::getEffectiveResolution(float & resolutionX, float & resolutionY)
{
	float scaleFactor = Game::getGuiScaleFactor();		
	resolutionX = (float) GLWindow::getClientWidth() / scaleFactor;
	resolutionY = (float) GLWindow::getClientHeight() / scaleFactor;	
}
