GALAXY.CPP

The galaxy.cpp file contains the code for GALAXIANS game logic and classes.
/****************************************************************
    GLAXIANS (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 <math.h>
#include "galaxy.h"
/***********************************************
   PLACE ALL CLASSES THAT ARE USED BY GLAXIANS
   GAME IN THIS FILE.
***********************************************/
st_pixel gal_formation::bullet_test_pixles[] = { 2,11 ,4,11 ,0,8 ,6,8, 2,0, 4,0 };
st_colors gal_formation::bullet_test_colors =  { 1, 15 };
st_bullet_params gal_formation::bullet_params = { 0 }; //  Will be initialized b4 game starts.

int gal_attack::attacks = 0;
int gal_attack::player_half_width = 0;
int gal_attack::lowest_y_shooting_point = 0;
int gal_attack::states[5][5] = { 2,2,0,8,8, 2,2,1,8,8, 3,3,5,7,7, 9,4,5,6,10, 9,9,5,10,10 };
int gal_attack::home_rotation_states[12] = { 4,4,4,3,3,3,2,2,2,1,1,0 };

#define ADD_LEFT_EXIT_ROTATION(MOVES,MOVE) \
{ \
    MOVE.x = 0; MOVE.y = -2; MOVE.times = 4; \
    MOVES.push_back(MOVE); \
    MOVE.x = -2; MOVE.y = -2; MOVE.times = 4; \
    MOVES.push_back(MOVE); \
    MOVE.x = -2; MOVE.y = 0; MOVE.times = 4; \
    MOVES.push_back(MOVE); \
    MOVE.x = -2; MOVE.y = 2; MOVE.times = 4; \
    MOVES.push_back(MOVE); \
    MOVE.x = 0; MOVE.y = 2; MOVE.times = 4; \
    MOVES.push_back(MOVE); \
}

#define ADD_RIGHT_EXIT_ROTATION(MOVES,MOVE) \
{ \
    MOVE.x = 0; MOVE.y = -2; MOVE.times = 4; \
    MOVES.push_back(MOVE); \
    MOVE.x = 2; MOVE.y = -2; MOVE.times = 4; \
    MOVES.push_back(MOVE); \
    MOVE.x = 2; MOVE.y = 0; MOVE.times = 4; \
    MOVES.push_back(MOVE); \
    MOVE.x = 2; MOVE.y = 2; MOVE.times = 4; \
    MOVES.push_back(MOVE); \
    MOVE.x = 0; MOVE.y = 2; MOVE.times = 4; \
    MOVES.push_back(MOVE); \
}
    
#define ADD_RIGHT_TO_LEFT_ROTATE(MOVES,MOVE,TOP_Y) \
{ \
      MOVE.x = 2; MOVE.y = 2; MOVE.times = 4; \
      MOVES.push_back(MOVE); \
      MOVE.x = 0; MOVE.y = 2; MOVE.times = 4; \
      MOVES.push_back(MOVE); \
      MOVE.x = -2; MOVE.y = 2; MOVE.times = 8; \
      MOVES.push_back(MOVE); \
      TOP_Y += 32; \
}
      
#define ADD_LEFT_TO_RIGHT_ROTATE(MOVES,MOVE,TOP_Y) \
{ \
      MOVE.x = -2; MOVE.y = 2; MOVE.times = 4; \
      MOVES.push_back(MOVE); \
      MOVE.x = 0; MOVE.y = 2; MOVE.times = 4; \
      MOVES.push_back(MOVE); \
      MOVE.x = 2; MOVE.y = 2; MOVE.times = 8; \
      MOVES.push_back(MOVE); \
      TOP_Y += 32; \
}

gal_attack::gal_attack(st_item **attack_sprites, unsigned int f_min, unsigned int f_max, int n)
{   /*  Build the attack pattern  */
    int i;
    
    home_rotation_state = 0;
    mode = GALAXY_ATTACK_MODE_ATTACKING;
    n_items = n;
    
    frame_rate_min = f_min;
    frame_rate_max = f_max;
    
    items = (st_attack_item *) calloc(n_items, sizeof(st_attack_item));  //  Delete in destructor.
    base_counter = galaxians->get_counter();
    
    for(i = 0; i < n_items; i++)
    {        
      items[i].item = attack_sprites[i];
      items[i].item->sprite->set_bg_flag();
      items[i].item->state = ITEM_IN_ATTACK;
      items[i].state_before_attack =  attack_sprites[i]->sprite->get_sprite_index();
      items[i].base_index = attack_sprites[i]->sprite->get_index_low();
    }
    
    leftmost_x = items[0].item->sprite->get_x_pos();
    top_y = items[0].item->sprite->get_y_pos();
    
    if(items[0].item->score == 100)
      if(n_items > 1)
        items[0].item->score <<= (n_items-1);
      
    fire_rate = 35 - (galaxians->get_level());
    if(fire_rate < 20)
      fire_rate = 20;
      
    fire_counter = fire_rate + (rand() & 7);
    attacks++;
    build_attack_script(G_TRUE);
}

void gal_attack::build_attack_script(int exit_from_formation)
{
    int x_inc;
    int y_inc;
    int t;    
    st_attack_move m;


    frame_rate = frame_counter = frame_rate_min;
    moves.clear();
    
    /*  if attack originate from leftmost/rightmost quarter of screen:  */
    /*  commit 1st type of attack, otherwise (middle) 2nd type.        */    
    if(!exit_from_formation)
    { //  Will go faster when attacking from top-screen.
      frame_rate = frame_counter = frame_rate_max;
      top_y = 0;  //  Start from top of screen.
    }
      
    y_inc = 2;
    if(leftmost_x < (SCREEN_W >> 1))
    {
      if(exit_from_formation)
        ADD_LEFT_EXIT_ROTATION(moves,m)
      x_inc = 4;
      if(leftmost_x > (SCREEN_W >> 2))
      {
        x_inc = 2; y_inc = 4;
      }
    }  
    else
    {
      if(exit_from_formation)
        ADD_RIGHT_EXIT_ROTATION(moves,m)
      x_inc = -4;
      if((SCREEN_W - leftmost_x) > (SCREEN_W >> 2))
      {
        x_inc = -2; y_inc = 4;
      }
    }  


    t = top_y >> 1;
    t += rand() % (SCREEN_H >> 3);
    if((top_y + (t * y_inc)) > SCREEN_H)
      t = ((SCREEN_H - top_y) / y_inc);
      
    /*  Add first attack phase  */  
    m.x = x_inc; m.y = y_inc; m.times = t;
    moves.push_back(m);
    top_y += (t * y_inc);

    /*  Transition from 1st phase to second  */
    if(x_inc < 0)
      ADD_LEFT_TO_RIGHT_ROTATE(moves,m,top_y)
    else
      ADD_RIGHT_TO_LEFT_ROTATE(moves,m,top_y)

    /*  Computer for 2nd phase.  */    
    x_inc = -x_inc;
    if(y_inc < 4)
    {
      x_inc /= 2;
      y_inc = 4;
    }
    else
      x_inc *= 2;

    t = ((SCREEN_H - top_y) / y_inc);
    if(t > 0)
    { /* Add second attack phase  */
      m.x = x_inc; m.y = y_inc; m.times = t;
      moves.push_back(m);
    }
    

    iter = moves.begin();
    times = iter->times;
    
    if(sound_flag & SOUND_GALAXY)
      play_sample((SAMPLE *) s_data[S_GALAXY_CHARCHING].dat, 192, 128, 1000, 0);

}

void gal_attack::remove(void)
{
    for(int i = n_items-1; i >= 0; i--)
      if(items[i].item->state != ITEM_IS_DEAD)
        items[i].item->sprite->remove();
}

void gal_attack::draw(void)
{
    for(int i = 0; i < n_items; i++)
      if(items[i].item->state != ITEM_IS_DEAD)
        items[i].item->sprite->draw();
}

void gal_attack::do_attack(void)
{   /*  Play the attack script  */
    st_attack_item *p_item;
    int i;
    int x_inc;
    int y_inc;
    int live_items;
    class enemy *shooting_item = NULL;

    if(frame_counter == 0)
    { //  Skip a frame every "frame_rate" count.
      frame_counter = frame_rate;
      return;
    }
    
    frame_counter--;
    switch(mode)
    {
      case GALAXY_ATTACK_MODE_ROTATE_BACK:
        live_items = 0;
        for(i = 0; i < n_items; i++)
        {
          p_item = &items[i];
          if(p_item->item->state != ITEM_IS_DEAD)
          {
           live_items++;
           p_item->item->sprite->set_sprite_index(p_item->base_index+home_rotation_states[home_rotation_state]);
           p_item->item->sprite->set_pos(p_item->item->x,p_item->item->y, G_FALSE);
          }
        }

        if(!live_items)  /* all are dead, destroy  */
        {
          mode = -1; //  Attack DONE!
          return;
        }
        
        if(home_rotation_state == 11)
        {
          for(i = 0; i < n_items; i++)
          {
            p_item = &items[i];
            if(p_item->item->state != ITEM_IS_DEAD)
            {
              p_item->item->sprite->set_sprite_index(p_item->state_before_attack);
              if((galaxians->get_counter() - base_counter) & 1)
                p_item->item->sprite->play_anim();  //  sync with everybody.
              p_item->item->sprite->clear_bg_flag();
              p_item->item->state = ITEM_IN_FORMATION;
              if(p_item->item->score > 100)
                p_item->item->score = 100;
            }    
          }
          mode = -1; //  Attack DONE!
          return;
        }
        
        home_rotation_state++;
        return;
        
      case GALAXY_ATTACK_MODE_BACK_HOME:          
          y_inc = 3;
          live_items = 0;
          for(i = 0; i < n_items; i++)
          {          
            p_item = &items[i];
            if(p_item->item->state != ITEM_IS_DEAD)
            {
              if(abs(p_item->item->sprite->get_y_pos() - p_item->item->y) < 5)
              { //  Transition to back-home-rotation state.
               mode = GALAXY_ATTACK_MODE_ROTATE_BACK;
               home_rotation_state = 0;
               return;
              }    
            
              live_items++;
              p_item->item->sprite->set_sprite_index(p_item->base_index+states[(y_inc >> 1) +2][2]);
              p_item->item->sprite->set_pos(p_item->item->x,p_item->item->sprite->get_y_pos() + y_inc, G_FALSE);
            }
          }
          
          if(!live_items)  /* all are dead, destroy  */
          {
            mode = -1; //  Attack DONE!
            return;
          }
          
        break;
                
      case GALAXY_ATTACK_MODE_ATTACKING:
        if(times)
        {
          live_items = 0;
          x_inc = iter->x;
          y_inc = iter->y;
          for(i = 0; i < n_items; i++)
          {
            p_item = &items[i];
            if(p_item->item->state != ITEM_IS_DEAD)
            {
              live_items++;
              shooting_item = p_item->item->sprite;
              shooting_item->set_sprite_index(p_item->base_index+states[(y_inc >> 1) +2][(x_inc >> 1) +2]);
              shooting_item->set_pos(shooting_item->get_x_pos() + x_inc,shooting_item->get_y_pos() + y_inc, G_FALSE);
            }
          }
          times--;
          
          if(!live_items)  /* all are dead, destroy  */
            mode = -1;     /*  Attack DONE!          */
          
          if(fire_counter)
            fire_counter--;
          else
          { //  Do fire.
            fire_counter = fire_rate + (rand() & 7);
            
            if(shooting_item)
            {
              int x_fire_source = shooting_item->get_x_pos()+((*game_sprites->images)[(*game_sprites).gal_bullet]->w >> 1);
              int y_fire_source = shooting_item->get_y_pos()+((*game_sprites->images)[shooting_item->get_sprite_index()]->h >> 1);
              int x_gap_1 = SCREEN_W;
              int x_gap_2 = SCREEN_W;

              if(y_fire_source < lowest_y_shooting_point)
              { //  Commit fire only over this point.
                if(player_1->get_lives())
                  x_gap_1 = (player_1->get_x_pos()+player_half_width) - x_fire_source;
                if(player_2->get_lives())
                  x_gap_2 = (player_2->get_x_pos()+player_half_width) - x_fire_source;

                if(abs(x_gap_1) <= abs(x_gap_2))
                  galaxians->bullet_params.x_inc = (int) (x_gap_1 / ((SCREEN_H - y_fire_source) / galaxians->bullet_params.y_inc));
                else
                  galaxians->bullet_params.x_inc = (int)(x_gap_2 / ((SCREEN_H - y_fire_source) / galaxians->bullet_params.y_inc));
                
                if(galaxians->bullet_params.x_inc > 4)
                  galaxians->bullet_params.x_inc = 4;
                if(galaxians->bullet_params.x_inc < -4)
                  galaxians->bullet_params.x_inc = -4;
                
                class bullet *p = new bullet(&(*game_sprites->images)[0], shooting_item->get_dest_bitmap(), NULL, (*game_sprites).gal_bullet, x_fire_source, y_fire_source, PLAYER_BULLET_Z_ORDER, G_TRUE, G_FALSE, galaxians->bullet_params);
                if(p)
                  e_bullets_list.push_back(p);
              }    
            }  
          }  
                      
          return;
        }
    
        if(iter != moves.end())
        {
          iter++;
          if(iter != moves.end())
          {
            times = iter->times;
          }  
          else
          { /*  Finished attack, go back home  */
            for(i = n_items-1; i >= 0; i--)
              if(items[i].item->state != ITEM_IS_DEAD)
              { //  Go back to top of screen.
                leftmost_x = items[i].item->x;
                items[i].item->sprite->set_pos(leftmost_x,items[i].item->sprite->get_y_pos() - SCREEN_H,G_FALSE);
              }  
            
            if((formation::get_attack_ok()) && (galaxians->get_live_items() <= (galaxians->get_max_attacks()+1)))
              return build_attack_script(G_FALSE);
            else
            { //  Slow down when going back home.
              frame_rate = frame_counter = frame_rate_min;  
              mode = GALAXY_ATTACK_MODE_BACK_HOME;
            }
            
            return;
          }
        }
        break;     

    }  /*  End of switch  */
}

gal_attack::~gal_attack()
{
    attacks--;
    free(items);
}

gal_formation::gal_formation(const int l, const st_formation_param &p) : formation(p), level(l)
{
    attack_counter = 0;
    
    frame_rate_index = 2;
    frame_rate_n_items[2] = (get_live_items() / 3) * 2;
    frame_rate_n_items[1] = get_live_items() / 3;
    frame_rate_n_items[0] = 0;

    if(level > 20)
      wave_rate = 10;
    else
      wave_rate = 50 - (level * 2);
    
    if(level < 9)
    { //  Moderate game under level 9.
      attack_min_speed = attack_max_speed = 6 + level;
    }
    else
    { //  Fast game from level 9.
      attack_min_speed = 15;
      attack_max_speed = 1000;
    }
      
    wave_counter = (wave_rate << 1);
    
    max_attacks = level;
    if(max_attacks > 5)
      max_attacks = 5;
      
    attack_row[0] = attack_row[1] = get_rows()-1;
};

void gal_formation::action()
{
    int l_items = get_live_items();
    if(l_items == 0)
      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--;
    }

/*****  Canceled, no sound for galaxian-moving-formation.
    if(gal_attack::attacks == 0)  //  Sound formation.
      if((!voice_check(S_GALAXY_FORMATION)) && (sound_flag & SOUND_GALAXY))  //  If currently NOT played.
        play_sample((SAMPLE *) s_data[S_GALAXY_FORMATION].dat, 255, 128, 1000, 0);
******/        

    //  Stop attack at full capacity.
    if(gal_attack::attacks == max_attacks)
      wave_counter = (wave_rate << 1);
      
    //  Let player get ready before going back to attack.
    if(!(attack_ok))
      wave_counter = 120;
      
    if(wave_counter)
    {
      wave_counter--;
      return formation::action();
    }
    
    /*  Let player rest every max-attacks  */
    if(attack_counter % max_attacks)
      wave_counter = wave_rate;
    else
      wave_counter = 120;

    /*  Finally it time to attack!  */    
    st_item *atk_items[3];
    int n_atk_items = 0;
    int atk_col;
    int *atk_row;
    *atk_items = NULL;
    
    if(attack_counter & 1)
    { /*  Attack starts from right.  */
      atk_row = &attack_row[1];
      if((attack_counter & 3) == 3)
        *atk_row = 0;  //  Jump to bonus every 3 attacks.
      for( ; ((!n_atk_items) && ((*atk_row) >= 0)) ; (*atk_row)--)
        for(atk_col = get_cols()-1 ; atk_col >= 0 ; atk_col--)
        { //  Once fond attack item break from both loops.
          *atk_items = get_item((*atk_row), atk_col);
          if(*atk_items)
            if((*atk_items)->state == ITEM_IN_FORMATION)
            {
              n_atk_items++;
              break;
            }  
        }
    }
    else
    {  /*  Attack starts from left.  */
       atk_row = &attack_row[0];
       if((attack_counter & 3) == 3)
         *atk_row = 0;  //  Jump to bonus every 3 attacks. 
       for( ; ((!n_atk_items) && (*atk_row) >= 0) ; (*atk_row)--)
        for(atk_col = 0 ; atk_col < get_cols(); atk_col++)
        { //  Once fond attack item break from both loops.
          *atk_items = get_item((*atk_row), atk_col);
          if(*atk_items)
            if((*atk_items)->state == ITEM_IN_FORMATION)
            {
              n_atk_items++;
              break;
            }  
        }           
    }
    
    (*atk_row)++;  //  Fix redundant decriment.
    if((n_atk_items) && ((*atk_row) == 0))
    { /*  Build triplet attack.  */
      if(atk_col)
        atk_col--;
        
      for(int j = 0; (j < 3); j++,atk_col++)
      {
        if(n_atk_items >= 3)
          break;                      
        if(atk_col == get_cols())
          break;
               
        atk_items[n_atk_items] = get_item(1, atk_col);
        if(atk_items[n_atk_items])
          if(atk_items[n_atk_items]->state == ITEM_IN_FORMATION)
            n_atk_items++;          
      }    
    }
      
    (*atk_row)--;      
    if((*atk_row) < 0)
      (*atk_row) = get_rows()-1;
     
    attack_counter++; 
    if(n_atk_items)  
    {
      class gal_attack *p = new class gal_attack(atk_items, attack_min_speed, attack_max_speed, n_atk_items);
      if(p)
        gal_attack_list.push_back(p);
    }  
    
    return formation::action();
};

gal_formation::~gal_formation()
{
};

class gal_formation *create_galaxian_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;
    if(!(game_type & BIT_GALAXIANS))
      return NULL;
      
    p.buffer = dst;  
    p.x_left_border = 0;
    p.x_right_border = SCREEN_W;
    p.y_top_border   = 0;
    p.y_bottom_border = SCREEN_H;

    p.frame_rate = 7;
    p.cols = GALAXY_COLS;
    if(game_type & BIT_INVADERS)
    {
      formation_y_start = GALVADERS_GALAXY_FORMATION_Y_START;
      p.rows = GAL_GALVADERS_ROWS;  //  GALVADERS game
    }  
    else  
    {
      formation_y_start = GAL_GALAXY_FORMATION_Y_START;
      p.rows = GALAXY_ROWS;
    }  

    moves = new st_formation_move[4];
    if(!moves)
      return NULL;
    
    // move right.    
    moves[0].x_inc = 4; moves[0].y_inc = 0; moves[0].times = 1000; moves[0].int_to_next_move = 1;
    //  move down 35 pix (1 row = 30 pix)
    moves[1].x_inc = 0; moves[1].y_inc = 5; moves[1].times = 6; moves[1].int_to_next_move = 1;
    // move left.    
    moves[2].x_inc = -4; moves[2].y_inc = 0; moves[2].times = 1000; moves[2].int_to_next_move = 1;
    //  move down 35 pix (1 row = 30 pix), and back to move right.
    moves[3].x_inc = 0; moves[3].y_inc = -5; moves[3].times = 6; moves[3].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 top_row_sprites[] = { 3, 6, 4, 5, 2, 7 };
    int offset,i ,j;
    int e_per_row;
    e_per_row = 1 + (level % 18);  //  1-more elian every 3 levels.
    if(e_per_row > 6)
      e_per_row = 6;

    /*  Do the top row (bonus items)  */
    e.index_low = index;
    e.index_high = e.index_low+1;
    e.anim_type = ANIM_LOW_HIGH;
    int t_score = 100;  //  Bonus items start at 100pts.
    for(i = 0; i < e_per_row; i++)
    {         
      list[top_row_sprites[i]].x = (top_row_sprites[i]) * GALAXY_W;
      list[top_row_sprites[i]].y = formation_y_start; // First row.
      list[top_row_sprites[i]].sample = S_GALAXY_BONUS;
      list[top_row_sprites[i]].state = ITEM_IN_FORMATION;
      list[top_row_sprites[i]].score = t_score; 
      list[top_row_sprites[i]].sprite = new class enemy(dt, dst, list[top_row_sprites[i]].x, list[top_row_sprites[i]].y, GALAXY_Z_ORDER, G_FALSE, G_TRUE, e);
      if(!(list[top_row_sprites[i]].sprite))  //  Failed to allocate, don't use this one.
        list[top_row_sprites[i]].state = ITEM_IS_DEAD;    
    }
    
#define DO_ROW_ITEMS(SAMPLE) { \
       offset = (p.cols*i)+j; \
       list[offset].x = j * GALAXY_W; \
       list[offset].y = (i * GALAXY_H) + formation_y_start; \
       list[offset].sample = SAMPLE; \
       list[offset].state = ITEM_IN_FORMATION; \
       list[offset].score = t_score; \
       list[offset].sprite = new class enemy(dt, dst, list[offset].x, list[offset].y, GALAXY_Z_ORDER, G_FALSE, G_TRUE, e); \
       if(!(list[offset].sprite)) \
         list[offset].state = ITEM_IS_DEAD; \
       }  

    /*  Do the 2nd row  */
    t_score = 40;
    e_per_row = 6; i = 1;
    e.index_low = index + GALAXY_INC_ROW_SPRITE;
    e.index_high = e.index_low+1;
    e.anim_type = ANIM_LOW_HIGH;    
    for(j = 2 ; j < (2+e_per_row) ; j++)
      DO_ROW_ITEMS(S_GALAXY_DEAD_FLY);
      
    /*  Do the 3rd, 4th rows  */
    t_score = 30;
    e_per_row = 8; i = 2;
    e.index_low = index + (2*GALAXY_INC_ROW_SPRITE);
    e.index_high = e.index_low+1;
    e.anim_type = ANIM_LOW_HIGH;    
    for(i = 2; i < 4; i++)
      for(j = 1 ; j < (1+e_per_row) ; j++)
        DO_ROW_ITEMS(S_GALAXY_DEAD_BEE);
        
      /*  Do 5,6 rows  */
    t_score = 20;
    e_per_row = 10;
    e.index_low = index + (3*GALAXY_INC_ROW_SPRITE);
    e.index_high = e.index_low+1;
    e.anim_type = ANIM_LOW_HIGH;    
    for(i = 4; i < p.rows; i++)
      for(j = 0 ; j < e_per_row ; j++)
        DO_ROW_ITEMS(S_GALAXY_DEAD_FLY);

    return new class gal_formation(level, p);
}
END_OF_FUNCTION(create_galaxian_formation);

/*  START - Class stars  */
stars::stars()
{
    dest = NULL;
    stars_info = NULL;
    colors = NULL;
    n_colors = 0;
};

stars::stars(BITMAP *d_bmp, int n, int x_low, int y_low, int x_high, int y_high, int y_speed, int f_show, int f_blank, int color, int d)
{
    dest = d_bmp;
    n_stars = n;
    x_min = x_low;
    y_min = y_low;
    x_max = x_high;
    y_max = inc_color_every = y_high;
    y_inc = y_speed;
    frames_show = f_show;
    frames_blank = f_blank;
    visible = d;

    n_colors = 1;
    colors = (int *) malloc(sizeof(int) * n_colors);
    memcpy(colors, &color, sizeof(int) * n_colors); 

    stars_info = create_stars();
    if(visible)
      draw();
}

stars::stars(BITMAP *d_bmp, int n, int x_low, int y_low, int x_high, int y_high, int y_speed, int f_show, int f_blank, int *col, int n_col, int col_s, int d)
{
    dest = d_bmp;
    n_stars = n;
    x_min = x_low;
    y_min = y_low;
    x_max = x_high;
    y_max = inc_color_every = y_high;
    y_inc = y_speed;
    frames_show = f_show;
    frames_blank = f_blank;
    visible = d;
    
    if(col_s)
      inc_color_every = (y_max - y_min) / col_s;
      
    n_colors = n_col;
    colors = (int *) malloc(sizeof(int) * n_colors);
    memcpy(colors, col, sizeof(int) * n_colors); 

    stars_info = create_stars();

    if(visible)
      draw();
}

stars::stars(const class stars &other)
{
    dest = other.dest;
    n_stars = other.n_stars;
    x_min = other.x_min;
    y_min = other.y_min;
    x_max = other.x_max;
    y_max = other.y_max;
    y_inc = other.y_inc;
    inc_color_every = other.inc_color_every;
    frames_show = other.frames_show;
    frames_blank = other.frames_blank;
    visible = other.visible;
    
    stars_info = NULL;
    colors = NULL;
    if(other.stars_info)
    {
      stars_info = (st_star *) malloc(n_stars * sizeof(st_star));
      memcpy(stars_info, other.stars_info, n_stars * sizeof(st_star));
    }
    if(other.colors)
    {
      colors = (int *) malloc(n_colors * sizeof(int));
      memcpy(colors, other.colors, n_colors * sizeof(int));
    }

    if(visible)
      draw();
}

void stars::remove(void)
{
    if(!stars_info)
      return;
            
    st_star *p = stars_info;
    st_star *e = &stars_info[n_stars];
    
    while(p < e)
    {

        if(p->visible)
        {  //  remove only stars that are not behind anything.
           dest->line[p->y][p->x] = 0;
           p->visible = G_FALSE;
        }
        p++;
    }
    
    visible = G_FALSE;
}

void stars::draw(void)
{
    if(!stars_info)
      return;
            
    st_star *p = stars_info;
    st_star *e = &stars_info[n_stars];
    unsigned int frames = frames_blank + frames_show;
    
    while(p < e)
    {
      if(p->frame)
      {  //  if using blinking-stars
        p->frame--;
        if(!p->frame)
          p->frame = frames;            
      }
        
      if(((p->frame > frames_blank) || p->frame == 0))
        if(!(dest->line[p->y][p->x]))
        {  //  Draw only stars that are not behind anything.
           dest->line[p->y][p->x] = p->color;
           p->visible = G_TRUE;
        }
      p++;
    }
    
    visible = G_TRUE;
}

void stars::move(void)
{
    if(!stars_info)
      return;
            
    st_star *p = stars_info;
    st_star *e = &stars_info[n_stars];
    
    while(p < e)
    {
        p->y += y_inc;
        if(p->y > y_max)
        { //  Reset y and change star-color
          p->y = (p->y - y_max); 
          p->next_inc_color = inc_color_every;
          p->color = colors[rand() % n_colors];
        }
          
        if((p->y) > (p->next_inc_color))
        {
          p->color++;
          p->next_inc_color += inc_color_every;
        }
        p++;
    }
}
  
stars::~stars()
{
    if(stars_info)
      free(stars_info);
      
    if(colors)
      free(colors);
}

st_star *stars::create_stars(void)
{
    st_star *star;
    st_star *p = (st_star *) malloc(n_stars * sizeof(st_star));
    if(!p)
      return NULL;
      
    memset(p, 0, n_stars * sizeof(st_star));

    int i,j;
    int tiles = (int) sqrt(n_stars);
    int tile_x = (x_max - x_min) / tiles;
    int tile_y = (y_max - y_min) / tiles;
    
    j = 0;
    star = p;
    st_star *e = &(p[n_stars]);

    while(star < e)
    { //  Spread stars evenly on sreen area.
      for(i = 0; i < tiles; i++)
      {
        star->x = (i*tile_x)+(rand() % tile_x);
        star->y = (j*tile_y)+(rand() % tile_y);
        star->frame = rand() % (frames_show + frames_blank);
        star->color = colors[rand() % n_colors];
        star->next_inc_color = inc_color_every;
        star++;
        if(star == e)
          break;
      }
      j++;
      if(j == tiles)
        j = 0;
    }
        
    return p;
}
/*  END   - Class stars  */