INV.CPP
The inv.cpp file contains the code for SPACE_INVADERS game logic and classes.
/****************************************************************
    SPACE INVADERS (V1.0), BY AHARON HILLEL.
    gcc version 3.2 (mingw special 20020817-1)
    ALLEGRO VERSION: 4.1.12 (WIP)
    ALLEGRO DATE:    2003-11-10
****************************************************************/
#include "inv.h"
/***********************************************
   PLACE ALL CLASSES THAT ARE USED BY INVADERS
   GAME IN THIS FILE.
***********************************************/
st_bullet_params inv_formation::bullet_params = { 0 }; //  Will be initialized b4 game starts.
st_pixel inv_formation::bullet_test_pixles[] = { 3,15, 4,15, 0,12, 7,12, 0,0, 7,0 };
st_colors inv_formation::bullet_test_colors =  { 1, 15 };

inv_formation::inv_formation(BITMAP *dst, const int l, const st_formation_param &p) : formation(p), level(l)
{
    dest = dst;
    RLE_SPRITE *m_ship_sprite = (*game_sprites->images)[(*game_sprites).inv_mother_ship];
    mother_ship.ship = NULL;
    mother_ship.time_for_mother_ship = MOTHER_SHIP_TIME_EXPR;
    mother_ship.e.index_low = (*game_sprites).inv_mother_ship;
    mother_ship.e.index_high = mother_ship.e.index_low + 3;
    mother_ship.e.x_min = -(m_ship_sprite->w);
    mother_ship.e.y_min = MOTHER_SHIP_Y_TOP;
    mother_ship.e.x_max = SCREEN_W + (m_ship_sprite->w);
    mother_ship.e.y_max = MOTHER_SHIP_Y_TOP + (m_ship_sprite->h);
    mother_ship.e.anim_type = ANIM_NONE;

    invaders_landed_on_earth = G_FALSE;
    next_bullet = 120;
    n_fire_options = 0;
    
    fire_options = (st_pixel *) malloc(get_cols() * sizeof(st_pixel));
    memset(fire_options, 0, get_cols());
    frame_rate_index = 7;
    frame_rate_n_items[7] = get_live_items() - (get_live_items() / 6);
    frame_rate_n_items[6] = get_live_items() - (get_live_items() / 4);
    frame_rate_n_items[5] = get_live_items() - (get_live_items() / 3);
    frame_rate_n_items[4] = get_live_items() - (get_live_items() / 2);
    frame_rate_n_items[3] = (get_live_items() / 2) - (get_live_items() / 4);
    frame_rate_n_items[2] = 4;
    frame_rate_n_items[1] = 2;
    frame_rate_n_items[0] = 0;
    
    s_frame_rate = get_frame_rate() << 2;
};

int inv_formation::destroy_mother_ship(int hit_x_pos)
{
    RLE_SPRITE *m_ship_sprite = (*game_sprites->images)[(*game_sprites).inv_mother_ship];
    int m_ship_half_w = (m_ship_sprite->w >> 1);
    int score;
    int x = mother_ship.ship->get_x_pos();
    int y = mother_ship.ship->get_y_pos();
    int criteria = (int) (m_ship_sprite->w / 6);
    
    class explosion *e = new explosion(&(*game_sprites->images)[0], dest, (*game_sprites).inv_mother_ship_explosion, (*game_sprites).inv_mother_ship_explosion+2, x+((m_ship_sprite->w) >> 1), y+((m_ship_sprite->h) >> 1), EXPLOSION_Z_ORDER, 4, G_TRUE, G_FALSE);
    if(e)
      explosions_list.push_back(e);

    /*  The more to the center - the higher-score  */
    if(abs(hit_x_pos - (x + m_ship_half_w)) > criteria)
    {
      score = 150;
      e = new explosion(&(*game_sprites->images)[0], dest, (*game_sprites).inv_mother_ship_150, (*game_sprites).inv_mother_ship_150+2, x+((m_ship_sprite->w) >> 1), y+((m_ship_sprite->h) >> 1), EXPLOSION_Z_ORDER, 10, G_TRUE, G_FALSE);
      if(e)
      explosions_list.push_back(e);

    }
    else
    {
      score = 300;
      e = new explosion(&(*game_sprites->images)[0], dest, (*game_sprites).inv_mother_ship_300, (*game_sprites).inv_mother_ship_300+2, x+((m_ship_sprite->w) >> 1), y+((m_ship_sprite->h) >> 1), EXPLOSION_Z_ORDER, 10, G_TRUE, G_FALSE);
      if(e)
        explosions_list.push_back(e);
    }

    if(sound_flag & SOUND_INVADERS)
    {
      stop_sample((SAMPLE *) s_data[S_INV_MOTHER_SHIP].dat);
      play_sample((SAMPLE *) s_data[S_INV_MOTHER_SHIP_EXP].dat, 255, 128, 1000, 0);
    }

    delete (mother_ship.ship);
    mother_ship.ship = NULL;
    mother_ship.time_for_mother_ship = MOTHER_SHIP_TIME_EXPR;
    
    return score;
};

void inv_formation::move_mother_ship(void)
{
    int x_pos = mother_ship.ship->get_x_pos();
    
    if((x_pos >= mother_ship.e.x_max) || (x_pos <= mother_ship.e.x_min))
    {  // Destroy mother ship, and start count for new one.
       delete (mother_ship.ship);
       mother_ship.ship = NULL;
       mother_ship.time_for_mother_ship = MOTHER_SHIP_TIME_EXPR;
       if(sound_flag)
         stop_sample((SAMPLE *) s_data[S_INV_MOTHER_SHIP].dat);

       return;
    }
    
    mother_ship.ship->play_anim();
    mother_ship.ship->set_pos(x_pos + mother_ship.x_inc, mother_ship.ship->get_y_pos(), G_FALSE);
}

void inv_formation::action()
{
    if(mother_ship.time_for_mother_ship)
    {
      mother_ship.time_for_mother_ship--;
      if((mother_ship.time_for_mother_ship == 0) && enemy::get_enemies())
      { //  Create mother ship.
        if(get_live_items() & 0x1)
        {  //  mother ship from right to left.
           mother_ship.e.anim_type = ANIM_HIGH_LOW;
           mother_ship.x_start = mother_ship.e.x_max;
           mother_ship.x_inc = -2;
        }
        else
        {  //  mother ship from left to right.
          mother_ship.e.anim_type = ANIM_LOW_HIGH;
          mother_ship.x_start = mother_ship.e.x_min;
          mother_ship.x_inc = 2;
        }
      
        mother_ship.ship = new enemy(&(*game_sprites->images)[0], dest, mother_ship.x_start+mother_ship.x_inc,  mother_ship.e.y_min,  0, G_FALSE, G_FALSE, mother_ship.e);
        if(sound_flag & SOUND_INVADERS)
          play_sample((SAMPLE *) s_data[S_INV_MOTHER_SHIP].dat, 72, 128, 1000, 1);

      }
    }
    
    if(mother_ship.ship)
      move_mother_ship();
      
    int l_items = get_live_items();
    if(l_items == 0)
      return;
    
    if(!(attack_ok))
      return;  //  Halt when no attack.
     
    if(test_formation_bottom())
    {  //  Invaders landed on EARTH.
       formation::clear_attack_ok();
       invaders_landed_on_earth = G_TRUE;
       return;
    }
    
    if(l_items < frame_rate_n_items[frame_rate_index])
    { //  Decriment frame-rate for faster move.
      set_frame_rate(get_frame_rate()-1);
      if(frame_rate_index)
        frame_rate_index--;
    }
    
    if(next_bullet)
      next_bullet--;
    else
    { //  Handle invaders fire
      next_bullet = (rand()) % 30;
      get_fire_options(fire_options, get_cols(), &n_fire_options);
      if(n_fire_options)
      { //  Do fire.
        int bullet_col = (rand() >> 3) % n_fire_options;      
        class bullet *p = new bullet(&(*game_sprites->images)[0], get_dest_bitmap(), NULL, (*game_sprites).inv_missile, fire_options[bullet_col].x-((*game_sprites->images)[(*game_sprites).inv_missile]->w >> 1), fire_options[bullet_col].y,  PLAYER_BULLET_Z_ORDER, G_TRUE, G_FALSE, bullet_params);
        if(p)
          e_bullets_list.push_back(p);  
      }
    }  
    
    s_frame_rate--;
    if(!s_frame_rate)
    {
       s_frame_rate = get_frame_rate() << 2;
       if(sound_flag & SOUND_INVADERS)
         play_sample((SAMPLE *) s_data[S_INV_FORMATION].dat, 64, 128, 1000, 0);
    }
    
    return formation::action();
};

inv_formation::~inv_formation()
{
    free(fire_options);
    if(mother_ship.ship)
      delete (mother_ship.ship);
};

class inv_formation *create_invaders_formation(unsigned char game_type, int level, RLE_SPRITE **dt, int index, BITMAP *dst)
{
    st_formation_param p;
    st_item *list;            //  To be deleted in formation destructor
    st_formation_move *moves; //  To be deleted in formation destructor
    int formation_y_start;
    int inv_types[INV_TYPES] = { 0, 1, 4, 11, 6, 7, 16, 8, 10, 15, 9, 5, 2, 14, 13, 12, 3, 17 };
    if(!(game_type & BIT_INVADERS))
      return NULL;

    p.frame_rate = 8;
    p.buffer = dst;      
    p.x_left_border = 0;
    p.x_right_border = SCREEN_W;
    p.y_top_border   = 0;
    p.y_bottom_border = GAME_FOOTER_BORDER_LINE-1;

    if(game_type & BIT_GALAXIANS)
    {
      formation_y_start = GAL_INVADERS_FORMATION_Y_START;
      p.cols = GAL_INVADERS_COLS;
      p.rows = INV_GLAVADERS_ROWS;  //  GALVADERS game
    }  
    else  
    {
      formation_y_start = INV_INVADERS_FORMATION_Y_START;
      p.cols = INVADERS_COLS;
      p.rows = INVADERS_ROWS;    
    }  

    moves = new st_formation_move[5];
    if(!moves)
      return NULL;
    
    //  Move down initially.
    int max_initial_moves_down = (((GAME_FOOTER_BORDER_LINE-1) - (formation_y_start+(p.rows*30))) / 30)-1;
    moves[0].x_inc = 0; moves[0].y_inc = 5;moves[0].int_to_next_move = 1;
    if(level < max_initial_moves_down)
      moves[0].times = 6 * level;
    else
      moves[0].times = max_initial_moves_down * 6;
    
    // move right.    
    moves[1].x_inc = 4; moves[1].y_inc = 0; moves[1].times = 1000; moves[1].int_to_next_move = 1;
    //  move down 1 row
    moves[2].x_inc = 0; moves[2].y_inc = 5; moves[2].times = 6; moves[2].int_to_next_move = 1;
    // move left.    
    moves[3].x_inc = -4; moves[3].y_inc = 0; moves[3].times = 1000; moves[3].int_to_next_move = 1;
    //  move down 1 row, and back to move right.
    moves[4].x_inc = 0; moves[4].y_inc = 5; moves[4].times = 6; moves[4].int_to_next_move = -3;

    list = new st_item[p.rows * p.cols];
    if(!list)
    {
      delete(moves);
      return NULL;
    }
    memset(list, 0, p.rows*p.cols*sizeof(st_item));
    
    /*  To be deleted when formation object comes out of scoop  */
    p.list = list;
    p.moves = moves;    

    /*  Allocate enemy sprites  */
    st_enemy_params e;
    e.x_min = e.y_min = 0;
    e.x_max = SCREEN_W;
    e.y_max = SCREEN_H;
   
    int offset;    
    for(int i = 0 ; i < p.rows ; i++)
      for(int j = 0; j < p.cols; j++)
      {  //  set st_enemy_params properly, then create the object.
         e.index_low = index+inv_types[(((level-1)+(p.rows-i-1)) / 2) % INV_TYPES];
         switch(e.index_low)
         {
           case 0 : // 0-10 are two phase, incremental index animation
           case 1 : // sprites.
           case 2 :
           case 3 :
           case 4 :
           case 5 :
           case 6 :
           case 7 :
           case 8 :
           case 9 :
           case 10: e.index_low *= 2;
                   e.index_high = e.index_low+1;
                   e.anim_type = ANIM_LOW_HIGH;
                   break;
             
           case 11: // 11, 12, 14,15,16 are four phase, ping-pong index animation
           case 12:
           case 14:
           case 15: //  sprites.
           case 16: e.index_low = 22+((e.index_low - 11)*4);
                    e.index_high = e.index_low+3;
                    e.anim_type = ANIM_PING_PONG;
                    break;
           case 13: // 13,17 are four phase, incremental index animation
           case 17: e.index_low = 22+((e.index_low - 11)*4);
                    e.index_high = e.index_low+3;
                    e.anim_type = ANIM_LOW_HIGH;
                    break;
                    
           default: e.index_low = index; e.index_high = e.index_low+1; e.anim_type = ANIM_LOW_HIGH;        
         } //  End of switch   
         offset = (p.cols*i)+j; 
         list[offset].x = j * INVADER_W;
         list[offset].y = formation_y_start + (INVADER_H * i);
         list[offset].sample = S_INV_DEAD_INVADER;
         list[offset].state = ITEM_IN_FORMATION;
         list[offset].score = (p.rows-i) * 5;
         list[offset].sprite = new class enemy(dt, dst, list[offset].x, list[offset].y, INVADERS_Z_ORDER, G_FALSE, G_TRUE, e);
         if(!(list[offset].sprite))  //  Failed to allocate, don't use this one.
           list[offset].state = ITEM_IS_DEAD;
      }    //  End of for(j...
      
      return new class inv_formation(dst, level, p);
}
END_OF_FUNCTION(create_invaders_formation);