/*
	Asteroids
    Copyright (C) 2006  Darragh Coy.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*##############################################################################

Module name: Bullet.cpp
Purpose:     Movement and collision detection for bullets.

##############################################################################*/

#include "Bullet.h"

/*******************************************************************************
Private module prototypes: Not for outside use!
*******************************************************************************/

   //..                                                                                                       

/*******************************************************************************
Variables
*******************************************************************************/

   //..

/*******************************************************************************

Name:       Bullet_Init
Purpose:    Initialises all bullets in the game as inactive
Parameters: Pointer to the gamedata
Returns:    None

*******************************************************************************/

void Bullet_Init ( GameData_t * gamedata )
{
  // Just zero off the bullet memory. This will set all bullets as inactive too.
  
  memset(gamedata->bullets,0,sizeof gamedata->bullets);
}

/*******************************************************************************

Name:       Bullet_Shift
Purpose:    Shifts all active bullets by the given amount
Parameters: Pointer to the gamedata, amount to shift them by x and y
Returns:    None

*******************************************************************************/

void Bullet_Shift ( GameData_t * gamedata , float xshift , float yshift )
{
     // Get pointer to bullet array
     
     Bullet_t * pBullet = gamedata->bullets;
     
     // Move all active bullets
     
     for ( int i = 0; i < BUL_MAX_BULLETS ; i++ )
     {
         // If this bullet is not active then  move on:
               
         if ( ! pBullet->bulletAlive ){ pBullet++ ; continue ; }
         
         // Shift the bullets position
         
         pBullet->position.x += xshift;
         pBullet->position.y += yshift;
         
         // Move onto the next bullet
         
         pBullet++;
     }
}

/*******************************************************************************

Name:       Bullet_Move
Purpose:    Moves all of the active bullets
Parameters: Pointer to the gamedata
Returns:    None

*******************************************************************************/

void Bullet_Move ( GameData_t * gamedata )
{
     // Get pointer to bullet array
     
     Bullet_t * pBullet = gamedata->bullets;
     
     // Move every bullet if alive:
             
     for ( int i = 0; i < BUL_MAX_BULLETS ; i++ )
     {
         // If this bullet is inactive then ignore
         
         if ( ! pBullet->bulletAlive ){ pBullet++; continue ; }

         // Otherwise move the bullet along:
                      
         pBullet->position.x += pBullet->velocity.x * Timer_FrameTime();
         pBullet->position.y += pBullet->velocity.y * Timer_FrameTime();
         
         // Get pointer to asteroids
         
         Asteroid_t * pAsteroid = gamedata->asteroids;
         
         // Check if it is in collision with any asteroids:
             
         for ( int i = 0 ; i < AST_MAX_ASTEROIDS * 2 ; i++ )
         {
             // Check if asteroid is active: if not then abort
                      
             if ( ! pAsteroid->asteroidAlive )
             {
                  // Asteroid not alive: do not check
                  
                  pAsteroid++ ; continue;
             }

             // Do collision check for this asteroid:
             
             if ( Asteroid_Inside(&pBullet->position,pAsteroid) )
             {
                  // Bullet in collision. See if asteroid is half or not:
                            
                  if ( pAsteroid->asteroidHalf )
                  {
                     // Half asteroid: just destroy it as normal
                     
                     pAsteroid->asteroidAlive = false;    
                     
                     // Add to player score points for destroying half asteroid
                     
                     gamedata->player.score += AST_SCORE_POINTS_HALF;
                     
                     // Create a particle explosion at the bullet position
                     
                     Particle_Explosion(&pBullet->position);
                                   
                  }
                  else
                  {
                      // Full asteroid: try and split it up
                      
                      Asteroid_Split(gamedata,pAsteroid);
                      
                     // Add to player score points for destroying full asteroid
                     
                     gamedata->player.score += AST_SCORE_POINTS_FULL;
                     
                     // Create a particle explosion at the bullet position
                     
                     Particle_Explosion(&pBullet->position);                                           
                  }        
                          
                  // Deactivate this bullet: it has now hit something
                                            
                  pBullet->bulletAlive     = false;
                  
                  // If player score best than his/her highest then save:
                        
                  if ( gamedata->player.score > gamedata->player.scoreHighest )
                  {
                       // Higher than player's own best: update
                       
                       gamedata->player.scoreHighest =  gamedata->player.score;
                  }
                  
                  // If player score better than all time record then save both
                  // name of player and the score
                        
                  if ( gamedata->player.score > highScore )
                  {
                       // Player has beaten all time record. Save score:
                       
                       highScore =  gamedata->player.score;
                       
                       // Save all time record holder name:
                               
                       memcpy(highScoreName,playerName,PLAYER_NAME_LENGHT+1);
                       
                  }                  

                  // Don't check for any more collisions. Bullet is now gone
                  
                  break;
                  
             } // end if bullet inside asteroid
             
             // Move onto next asteroid
             
             pAsteroid++;
         }   
    
         // Check if bullet offscreen: if so then deactivate
         
         if ( pBullet->position.x < -BUL_OFFSCREEN_DIST )
         {
              // Offscreen to the left. Deactivate
              
              pBullet->bulletAlive = false; continue;
         }                       
         
         if ( pBullet->position.x > WINDOW_X + BUL_OFFSCREEN_DIST )
         {
              // Offscreen to the right. Deactivate
              
              pBullet->bulletAlive = false; continue;
         } 
         
         if ( pBullet->position.y < - BUL_OFFSCREEN_DIST )
         {
              // Offscreen to the top. Deactivate
              
              pBullet->bulletAlive = false; continue;
         } 
         
         if ( pBullet->position.y > WINDOW_Y + BUL_OFFSCREEN_DIST )
         {
              // Offscreen to the bottom. Deactivate
              
              pBullet->bulletAlive = false; continue;
         }                                  
  
         // Move onto the next bullet
         
         pBullet++;
         
     } // End move every bullet loop
}

/*******************************************************************************

Name:       Bullet_Create
Purpose:    Creates a bullet in a given direction at a given point
Parameters: Pointer to the gamedata , bullet position, bullet direction
Returns:    None

*******************************************************************************/

void Bullet_Create ( GameData_t * gamedata , Vec2D_t * pos , float angle  )
{
    // Pointer to in bullet array
    
    Bullet_t * pBullet = gamedata->bullets;
     
    // Search for free position in bullet array:
               
    int freePos = -1;
    
    for ( int i = 0 ; i < BUL_MAX_BULLETS ; i++ )
    {
        // Check if bullet active:
        
        if ( ! pBullet->bulletAlive )
        {
             // Not active: save index and abort search for free position
             
             freePos = i; break;
        }
        
        // Move onto next bullet
        
        pBullet++;
    }
    
    // If not found free position abort creation
    
    if ( freePos < 0 ) return;
    
    // Set bullet position:
           
    pBullet->position.x =  pos->x;
    pBullet->position.y =  pos->y;
    
    // Set bullet movement vector:
           
    pBullet->velocity.x = cosf( angle * TO_RADIANS ) * BUL_SPEED;
    pBullet->velocity.y = sinf( angle * TO_RADIANS ) * BUL_SPEED;    
    
    // Set bullet as active: 
           
    pBullet->bulletAlive = true;
}

/*******************************************************************************

Name:       Bullet_Draw
Purpose:    Draws all bullets
Parameters: Pointer to game data
Returns:    None

*******************************************************************************/

void Bullet_Draw ( GameData_t * gamedata )
{
     // Pointer to current bullet
     
     Bullet_t * pBullet = gamedata->bullets;
     
     // Draw all active bullets
     
     for ( int i = 0 ; i < BUL_MAX_BULLETS ; i++ )
     {
         // Check if bullet active: dont draw if not
         
         if ( ! pBullet->bulletAlive ){ pBullet++; continue ; }
         
         // Get normalised vector in opposite direction of bullet movement
         
         Vec2D_t tailVec; 
         
         tailVec.x = -pBullet->velocity.x;
         tailVec.y = -pBullet->velocity.y; 
         
         Vec2D_Normalise(&tailVec);
         
         // Multiply tail vector components by tail lenght
         
         tailVec.x *= BUL_TAIL_SIZE;        
         tailVec.y *= BUL_TAIL_SIZE;    
         
         // Set the line width
         
         glLineWidth(BUL_WIDTH);
         
         // Draw the bullet line: End of tail is in black, head of bullet in white
         
         glBegin(GL_LINES);
         
            glColor3f  (1.0,1.0,1.0);
            glVertex2f (pBullet->position.x,pBullet->position.y);
            
            glColor3f  (0.0,0.0,0.0);
            glVertex2f (pBullet->position.x+tailVec.x,pBullet->position.y+tailVec.y);  
                   
         glEnd();
         
         // Reset the line width
         
         glLineWidth(1.0);
         
         // Move onto next bullet
         
         pBullet++;
     
     } // End draw all bullets loop

}
