MENU.CPP
The menu.cpp file contains the implementation of the menu-class.
/****************************************************************
    GALVADERS (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 "keys.h"
#include "menu.h"
#include "inv.h"
#include "galaxy.h"

char *menu::game_names[] = { "MUTE", "SPACE INVADERS", "GALAXIANS", "GALVADERS" };
char menu::default_menu_keys[] = { M_KEY_ESCAPE, M_KEY_START_1, M_KEY_START_2, M_KEY_SOUND, M_KEY_PAUSE, M_KEY_CHANGE_CONTROLS, M_KEY_SWITCH_GAME, M_KEY_VSYNC, M_KEY_SHIELDS, M_KEY_BULLETS };
#define NUMBER_OF_MENU_KEYS 10

menu::menu()
{
    menu_status_register = sound_ok = sound_expr = sound_inc = pause_after_get_window_focus = 0;
    dest = game_footer = NULL;
    n_keys = NUMBER_OF_MENU_KEYS;      
    menu_keys = (char *) malloc(n_keys * sizeof(char));
    memcpy(menu_keys, default_menu_keys,n_keys * sizeof(char));
    read_files();
}

menu::menu(BITMAP *dest_bmp, unsigned int s_ok_max)
{
    menu_status_register = pause_after_get_window_focus = 0;
    sound_ok = s_ok_max;
    game_footer = NULL;
    dest = dest_bmp;
    n_keys = NUMBER_OF_MENU_KEYS;
        
    menu_keys = (char *) malloc(n_keys * sizeof(char));
    memcpy(menu_keys, default_menu_keys,n_keys * sizeof(char));
    read_files();    
    do_arcade_reset(BIT_INVADERS | BIT_GALAXIANS);
    SET_BULLETS_BITS(2);  //  Default is 4-bullets.
}

menu::menu(BITMAP *dest_bmp, unsigned int s_ok_max, int *m_keys, int n)
{   /*  For future apps: Flexible menu.  */
    menu_status_register = pause_after_get_window_focus = 0;
    sound_ok =  s_ok_max;
    game_footer = NULL;
    dest = dest_bmp;
    n_keys = n;
    menu_keys = (char *) malloc(n_keys * sizeof(char));
    memcpy(menu_keys, m_keys, n_keys * sizeof(char));
    read_files();
    do_arcade_reset(BIT_INVADERS | BIT_GALAXIANS);
    SET_BULLETS_BITS(2);  //  Default is 4-bullets.
}

void menu::get_key_name(char key, char *name)
{
    strcpy(name, "Undefined");
    if(key < PAD_KEYS)
    {
      sprintf(name, "%c", key+'A'-1);
    }

    if((key >= DIGIT_KEYS) && (key < PAD_KEYS))
      sprintf(name, "%c", (key-DIGIT_KEYS)+'0');

    if((key >= PAD_KEYS) && (key < KEY_MAX))
      strcpy(name, key_names[key-PAD_KEYS]);
}

void menu::do_arcade_reset(unsigned char game_type)
{
    game_font = INV_FONT;
    menu_state = STATE_SHOW_CONTROLS;
    
    player_1_last_game_score = player_2_last_game_score = 0;
    if(stars_layer_1)
      delete stars_layer_1;
      
    stars_layer_1 = NULL;
    SET_GAME_TYPE(game_type);

    if(game_footer)
      destroy_bitmap(game_footer);
      
    game_footer = NULL;
      
    int i;
    for(i = 0; i <= SCREEN_W; i += (int) (SCREEN_W / 16))
      rectfill(dest,i-2, 0 , i+2, SCREEN_H, 143);
    for(i = 0; i <= SCREEN_H; i += (int) (SCREEN_H / 16))
      rectfill(dest,0, i-2, SCREEN_W, i+2,143);
     
    acquire_screen();
    vsync();
    blit(dest, screen, 0, 0, 0, 0 , SCREEN_W, SCREEN_H);
    release_screen();

    i = 0;
    time_for_frame = 0;

    while(i < 25)
    {
       if(time_for_frame)
       {
         i++;
         time_for_frame = G_FALSE;
       }
    } 

    clear_bitmap(dest);
    
    sound_expr = SOUND_INVADERS;
    sound_flag = (sound_expr & sound_ok);
    sound_inc = (SOUND_INVADERS_INC & sound_ok);
    if(menu_status_register & BIT_GALAXIANS)
    { //  Galaxy Game.
      int stars_fading_out;
      int stars_y_max;
      
      sound_expr = SOUND_GALAXY;
      sound_flag = (sound_expr & sound_ok);
      sound_inc = (SOUND_GALAXY_INC & sound_ok);
      if(menu_status_register & BIT_INVADERS)
      { //  GalVaders Game.
        sound_expr = SOUND_GALVADERS;
        sound_flag = (sound_expr & sound_ok);
        sound_inc = SOUND_INVADERS_INC;
        stars_y_max = GAME_FOOTER_BORDER_LINE;
        stars_fading_out = 5;
      }
      else
      {
        stars_y_max = SCREEN_H-1;
        stars_fading_out = 0;
      }
        
      game_font = GAL_FONT;
      int s_colors[] = { 215, 220, 225, 230, 235, 240, 245, 250 };
      stars_layer_1 = new stars(dest, 96, 0, 0, SCREEN_W-1, stars_y_max, 2, 30, 10, s_colors, 8, stars_fading_out, G_FALSE);
    }

    menu_status_register &= CLEAR_SHIELDS;
    if(menu_status_register & BIT_INVADERS)
    { //  For any game with space-invaders, game-footer separated from screen.
      game_footer = create_bitmap(dest->w, (dest->h)-GAME_FOOTER_BORDER_LINE);
      clear_bitmap(game_footer);
      menu_status_register |= BIT_SHIELDS;
    }
    else
    { //  in a pure-galaxian game, game footer is a part of screen.
      game_footer = create_sub_bitmap(dest, 0, GAME_FOOTER_BORDER_LINE, dest->w, (dest->h)-GAME_FOOTER_BORDER_LINE);
    }
}

void menu::read_files(void)
{
    FILE *fp;
    char *file_name;
    
    st_high_score_record def_list[N_HIGH_SCORES] = { "SPACE INVADERS", 2500, "BY", 2000, "AHARON HILLEL", 1500, "aharonh.tripod.com", 1000, "FOR MORE INFO", 500, "GALAXIANS", 25000, "BY", 20000, "AHARON HILLEL", 15000, "aharonh.tripod.com", 10000, "FOR MORE INFO", 5000, "GALVADERS", 30000, "BY", 25000, "AHARON HILLEL", 20000, "aharonh.tripod.com", 15000, "FOR MORE INFO", 10000 };
    /*  First do defaults, then read from files if exist  */
    player_keys[0].left = KEY_Z;
    player_keys[0].right = KEY_X;
    player_keys[0].fire = KEY_SPACE;
    player_keys[1].left = KEY_LEFT;
    player_keys[1].right = KEY_RIGHT;
    player_keys[1].fire = KEY_0_PAD;
    
    file_name = (char *) malloc((strlen((*game_sprites).dir)) + (strlen(CONTROLS_FILE_NAME)) + (strlen(HIGH_SCORES_FILE_NAME))+1);
    sprintf(file_name,"%s%s",(*game_sprites).dir, CONTROLS_FILE_NAME); 
    if((fp = fopen(file_name, "rb")) != NULL)
    {
      fread(&player_keys[0], sizeof(st_controls), 2, fp);
      fclose(fp);
    }
    
    memcpy(high_scores, def_list, (sizeof(st_high_score_record)*N_HIGH_SCORES));
    sprintf(file_name,"%s%s",(*game_sprites).dir, HIGH_SCORES_FILE_NAME); 
    if((fp = fopen(file_name, "rb")) != NULL)
    {
      fread(&high_scores[0], sizeof(st_high_score_record), N_HIGH_SCORES, fp);
      fclose(fp);
    }    
    
    /*  LOAD DEMO-FILES. First clear all demos, then read files.  */
    memset(game_demo, 0, sizeof(short *) * NUMBER_OF_DEMOS);
    int i;
    short n;
    char *demo_files[] = { INVADERS_DEMO_FILE, GALAXY_DEMO_FILE, GALVADERS_DEMO_FILE };
    
    for(i = 0; i < NUMBER_OF_DEMOS; i++)
    {
      sprintf(file_name,"%s%s",(*game_sprites).dir, demo_files[i]);
      if((fp = fopen(file_name, "rb")) != NULL)
      {
        n = 0;
        fread(&n, sizeof(short), 1, fp);
        game_demo[i] = (unsigned short *) malloc(n * sizeof(unsigned short));
        if(game_demo[i])
        {
          *game_demo[i] = n;
          fread(&(game_demo[i][1]), sizeof(short), n-1, fp);
        }
        fclose(fp);
      }    
    }
    free(file_name);
}

unsigned char menu::get_key(void)
{
    int i;
    poll_keyboard();
    for(i = 1 ; i < KEY_MAX ; i++)
      if(key[i])
        break;

    return i;
}

void menu::get_change_controls_text(unsigned char *key_set ,char *text)
{
    char *title[] = { "LEFT ", "RIGHT ", "FIRE " };
    char key_name[16];
    int i;
    strcpy(text, " PLAYER ONE \n\n");
   
    for(i = 0; i < sizeof(st_controls); i++)
    {
      get_key_name(key_set[i], key_name);
      strcat(text," ");
      strcat(text, title[i]);
      strcat(text, key_name);
      strcat(text," \n");
    }
    
    strcat(text,"\n PLAYER TWO \n\n");
    for(i = sizeof(st_controls); i < sizeof(st_controls)*2; i++)
    {
      get_key_name(key_set[i], key_name);
      strcat(text," ");
      strcat(text, title[i-sizeof(st_controls)]);
      strcat(text, key_name);
      strcat(text," \n");
    }
}

#define WINDOW_WT 24
#define WINDOW_HT 15
#define COMMAND_TEXT      " TYPE PLAYER %s KEY FOR %s "
#define COMMAND_ERR_MENU  " KEY '%s' RESERVED FOR MENU, try Again "
#define COMMAND_ERR_DEF   " KEY '%s' IS DEFINED, try Again "
int menu::change_controls(void)
{
    char *player_name[] = { "ONE", "TWO" };
    char *control_to_define[] = { "LEFT", "RIGHT", "FIRE" };
    char buf[16];
    unsigned char key;
    int i;
    int err_flag = G_FALSE;
    unsigned char p_keys[sizeof(st_controls)*2];
    
    char temp_t[1024];
    memset(temp_t, ' ', WINDOW_WT);
    memset(&temp_t[WINDOW_WT], '\n',WINDOW_HT);
    *(temp_t+WINDOW_WT+WINDOW_HT) = '\0';
    text_msg window_bg(dest, temp_t, SCREEN_W / 2, SCREEN_H / 2, GAL_FONT, G_TRUE, 5, 9, TEXT_CENTER, TEXT_CENTER, (*game_sprites).game_logo, G_FALSE);
    text_msg window_header(dest, " CHANGE CONTROLS ", SCREEN_W / 2, (window_bg.get_y_pos() - (window_bg.get_msg_height() / 2)+20), MENU_FONT, G_TRUE, 246, -1, TEXT_CENTER, TEXT_BOTTOM, NULL, G_FALSE);
    text_msg window_text(dest, temp_t, window_bg.get_x_pos() - (window_bg.get_msg_width() / 2) + 20, (window_bg.get_y_pos() - (window_bg.get_msg_height() / 2)+50), MENU_FONT, G_TRUE, 234, -1, TEXT_RIGHT, TEXT_BOTTOM, NULL, G_FALSE);
    text_msg user_command(dest, temp_t, SCREEN_W / 2, (window_bg.get_y_pos() + (window_bg.get_msg_height() / 2) - 30), MENU_FONT, G_TRUE, 226, -1, TEXT_CENTER, TEXT_CENTER, NULL, G_FALSE);
    /*  First make a copy of currnet key setting  */    
    memcpy(p_keys, player_keys, sizeof(st_controls)*2);
    window_bg.draw();
    window_header.draw();

    for(i = 0; i < sizeof(st_controls)*2 ; i++)
    {
      get_change_controls_text(p_keys, temp_t);
      window_text.set_text(temp_t);
      window_text.draw();

      if(!err_flag)
      { //  In no previous error, display command.
        sprintf(temp_t,COMMAND_TEXT, player_name[(i) / sizeof(st_controls)], control_to_define[(i) % sizeof(st_controls)]);
        user_command.set_text(temp_t);
        user_command.set_text_color(226);
      }
      
      err_flag = G_FALSE;
      user_command.draw();
      
      acquire_screen();
      if(menu_status_register & BIT_VSYNC)
        vsync();
      blit(dest, screen, 0, 0, 0, 0 , SCREEN_W, SCREEN_H);
      release_screen();
      time_for_frame = G_FALSE;
      do
      {
         key = get_key();
         while(get_key() ==  key);  //  Wait for key release.
      }while(key == KEY_MAX);
      window_text.remove();
      user_command.remove();
      
      if(key == KEY_ESC)
        break;  //  User choose to cancel

      if(memchr(menu_keys, key, n_keys))
      { //  Show error, conflict with menu.
        get_key_name(key, buf);
        sprintf(temp_t, COMMAND_ERR_MENU, buf);
        user_command.set_text(temp_t); 
        user_command.set_text_color(246);
        err_flag = G_TRUE; //  Error will be diplayed on next loop exe.
        i--;
        continue;
      }
      if(memchr(p_keys, key, i))
      { //  Show error, conflict with pre-defined key.
        get_key_name(key, buf);
        sprintf(temp_t, COMMAND_ERR_DEF, buf);
        user_command.set_text(temp_t); 
        user_command.set_text_color(246);
        err_flag = G_TRUE;  //  Error will be diplayed on next loop exe.
        i--;
        continue;
      }
      
      p_keys[i] = key;
    }  /*  End for(i...  */
    window_header.remove();
    window_bg.remove();
    
    if(i < (sizeof(st_controls)*2))  //  This means cancel
      return G_FALSE;  //  User hit Escape before loop ended.
      
    /*  Save changes  */  
    memcpy(player_keys,p_keys, sizeof(st_controls)*2);
    FILE *fp;
    char *file_name;
    file_name = (char *) malloc((strlen((*game_sprites).dir)) + (strlen(CONTROLS_FILE_NAME))+1);
    if(file_name)
    { //  Save changes in file.
      sprintf(file_name,"%s%s",(*game_sprites).dir, CONTROLS_FILE_NAME); 
      if((fp = fopen(file_name, "wb")) != NULL)
      {
        fwrite(&player_keys[0], sizeof(st_controls), 2, fp);
        fclose(fp);
      }

      free(file_name);
    }

    return G_TRUE;
}

int menu::test_menu_keys(void)
{
    poll_keyboard();
    for(int i = 0; i < n_keys; i++)
      if(key[menu_keys[i]])
      {
        while(key[menu_keys[i]])
          poll_keyboard();  //  Wait for release of menu-key
        return menu_keys[i];
      }
      
    if(pause_after_get_window_focus & SIMULATE_PAUSE_AFTER_GET_FOCUS)
    {
      pause_after_get_window_focus = 0;
      if(!(menu_status_register & BIT_GAME_OVER))
        return M_KEY_PAUSE;
    }
      
    return 0;
}

void menu::clear_menu_items(void)
{
    m_sprite_list::iterator del_s_iter;
    m_msg_list::iterator del_m_iter;

    RUN_LIST_REVERSE(s_list, s_iter,sprite->remove);
    RUN_LIST_REVERSE(m_list, m_iter,text->remove);
    if(s_list.size())  /*  if list is NOT empty.  */
    {
      s_iter = s_list.begin();
      while(s_iter != s_list.end())
      {
        del_s_iter = s_iter;
        s_iter++;
        delete((*del_s_iter)->sprite);
        free(*del_s_iter);
        s_list.erase(del_s_iter);
      } /*  End of while loop  */
    }
    if(m_list.size())  /*  if list is NOT empty.  */
    {
      m_iter = m_list.begin();
      while(m_iter != m_list.end())
      {
        del_m_iter = m_iter;
        m_iter++;
        delete((*del_m_iter)->text);
        free(*del_m_iter);
        m_list.erase(del_m_iter);
      } /*  End of while loop  */
    }
}

void menu::stop_sound(unsigned int old_sound_flag)
{
    if((sound_flag ^ old_sound_flag) & SOUND_INVADERS)
    {  //  Need to stop invaders-sound.
       stop_sample((SAMPLE *) s_data[S_INV_MOTHER_SHIP].dat);
    }
    
    if((sound_flag ^ old_sound_flag) & SOUND_GALAXY)
    {  //  Need to stop galaxy-sound.
       stop_sample((SAMPLE *) s_data[S_GALAXY_CHARCHING].dat);
    }
}

int menu::play_menu(unsigned int frames, class text_msg *controls_text)
{
    int menu_key = test_menu_keys();
    int frame_computed = G_FALSE;
    int anim_counter = 0;
    unsigned char flag;
    char text[1024];

    while(frames && (!menu_key))
    {
       if(!frame_computed)
       {
         if(stars_layer_1)
         {
           stars_layer_1->remove();
           stars_layer_1->move();
         }
         anim_counter++;

         if(s_list.size())  /*  if list is NOT empty. */
         {
          s_iter = s_list.end();
          do{
              s_iter--;
              (*s_iter)->sprite->remove();
              if((anim_counter % 10) == 0)
                (*s_iter)->sprite->play_anim();
              if((*s_iter)->times)
              {
                (*s_iter)->sprite->set_pos((*s_iter)->sprite->get_x_pos()+(*s_iter)->x_inc, (*s_iter)->sprite->get_y_pos()+(*s_iter)->y_inc, G_FALSE);
                (*s_iter)->times--;
              }  
            }while(s_iter != s_list.begin());
         }
         
         if(m_list.size())  /*  if list is NOT empty. */
         {
          m_iter = m_list.end();
          do{
              m_iter--;
              (*m_iter)->text->remove();
              if((*m_iter)->times)
              { //  Text is moving.
                (*m_iter)->text->set_pos((*m_iter)->text->get_x_pos()+(*m_iter)->x_inc, (*m_iter)->text->get_y_pos()+(*m_iter)->y_inc, G_FALSE);
                (*m_iter)->times--;
              }
            }while(m_iter != m_list.begin());
         }

         menu_key = test_menu_keys();
         if(controls_text)
         {  //  Controls-text is shown on screen, update "on-the-fly"
           if(menu_key == M_KEY_VSYNC)
           {
             menu_status_register ^= BIT_VSYNC;
             controls_text->set_text(get_controls_text(text));
             menu_key = 0;
           }

           if(menu_key == M_KEY_SHIELDS)
           {
             menu_status_register ^= BIT_SHIELDS;
             controls_text->set_text(get_controls_text(text));
             menu_key = 0;
           }
          
           if(menu_key == M_KEY_BULLETS)
          {
             flag = GET_BULLETS_BITS;
             if(flag < 3)
               flag++;
             else
               flag = 0;
              
             SET_BULLETS_BITS(flag);
             controls_text->set_text(get_controls_text(text));
             menu_key = 0;
           }
           
           if(menu_key == M_KEY_SOUND)
           {
             sound_flag = (sound_flag + sound_inc) & sound_expr;
             controls_text->set_text(get_controls_text(text));
             menu_key = 0;
           }
         }
         
         
         RUN_LIST_INCREMENTAL(m_list, m_iter,text->draw);
         RUN_LIST_INCREMENTAL(s_list, s_iter,sprite->draw);
         
         if(stars_layer_1)
           stars_layer_1->draw();

         frame_computed = G_TRUE;
       }
         
       /*****   START  --  SAVE SCREENSHOT  ****
       BITMAP *last_frame;
       
       char screenshot_file_name[128];
       sprintf(screenshot_file_name, "%s%s_%ld.bmp", (*game_sprites).dir, game_names[menu_status_register & 3], time(NULL));
       if(key[KEY_F12])
       {
         last_frame = create_bitmap(SCREEN_W, SCREEN_H);
         acquire_screen();
         if(last_frame)  //  This will be used when getting focus back in pause-mode
           blit(screen, last_frame, 0, 0, 0, 0 , SCREEN_W, SCREEN_H);
         release_screen();

         save_bitmap(screenshot_file_name, last_frame, (RGB *) s_data[GAME_P].dat);
         if(last_frame)
           destroy_bitmap(last_frame);
       }
        *****   END    --  SAVE SCREENSHOT  */

		
       if(time_for_frame) // if(time_for_frame)
       {
         acquire_screen();
         if(menu_status_register & BIT_VSYNC)
           vsync();
         blit(dest, screen, 0, 0, 0, 0 , SCREEN_W, SCREEN_H);
         release_screen();

         time_for_frame = G_FALSE;
         frame_computed = G_FALSE;
         frames--;
       }
    }
    
    return menu_key;
}

void menu::remove()
{
    RUN_LIST_REVERSE(s_list, s_iter,sprite->remove);
    RUN_LIST_REVERSE(m_list, m_iter,text->remove);
}

void menu::draw()
{
    RUN_LIST_INCREMENTAL(m_list, m_iter,text->draw);
    RUN_LIST_INCREMENTAL(s_list, s_iter,sprite->draw);
}

/*  MACRO to add text to menu:
    TEXT_ITEM - name for struct text_menu_item *
    X_INC     - Increment value for moving the TEXT on X
    Y_INC     - Increment value for moving the TEXT on Y
    TIMES     - How many times to move the TEXT (using X_INC, Y_INC)
    DEST_BITMAP - Bitmap to draw TEXT.
    TEXT      - Text to draw
    X_POS     - The starting point for positioning the TEXT
    Y_POS     - The starting point for positioning the TEXT
    FONT_INDEX - font index from DAT file.
    FLOATING_TEXT - TEXT moving on top of Z-ORDER not clearing background
    TEXT_COLOR - The name says it all
    TEXT_BG_COLOR - Use (-1) value of transperent TEXT
    X_TEXT_ALGN - How to align the text on X_POS. Use TEXT_RIGHT, TEXT_CENTER and TEXT_LEFT for this one
    Y_TEXT_ALGN - How to align the text on Y_POS. Use TEXT_BOTTOM, TEXT_CENTER and TEXT_TOP for this one
    TILE        - Pointer to BITMAP that will be used as background to the TEXT.
    VISIBLE_FLAG - if the flag is set, show TEXT after allocating it.
*/
#define ADD_TEXT_MENU_ITEM(TEXT_ITEM,X_INC,Y_INC,TIMES,DEST_BITMAP,TEXT,X_POS,Y_POS,FONT_INDEX,FLOATING_TEXT,TEXT_COLOR,TEXT_BG_COLOR,X_TEXT_ALGN,Y_TEXT_ALGN,TILE,VISIBLE_FLAG) \
{ \
TEXT_ITEM = (text_menu_item *) malloc(sizeof(text_menu_item)); \
TEXT_ITEM->x_inc = X_INC; TEXT_ITEM->y_inc = Y_INC; TEXT_ITEM->times = TIMES; \
TEXT_ITEM->text = new class text_msg(DEST_BITMAP, TEXT, X_POS, Y_POS, FONT_INDEX, FLOATING_TEXT, TEXT_COLOR, TEXT_BG_COLOR, X_TEXT_ALGN, Y_TEXT_ALGN, TILE, VISIBLE_FLAG); \
m_list.push_back(TEXT_ITEM); \
}

/*  MACRO to add sprite to menu:
    SPRITE_ITEM - name for struct sprite_menu_item *
    SPRITE_PTR  - pointer to class enemy object.
    SPRITE_LIST - pointer to RLE_SPRITE ** array from where to fetch the sprite bitmap
    SPRITE_INDEX_LOW  - Index for 1st sprite bitmap
    SPRITE_INDEX_HIGH - Index for last sprite bitmap
    X_MIN     - Leftmost location for enemy (could be off-screen).  Macro will set E_PARAMS.x_min with this value
    X_MAX     - Rightmost location for enemy (could be off-screen).  Macro will set E_PARAMS.x_max with this value
    Y_MIN     - Top location for enemy (could be off-screen).  Macro will set E_PARAMS.y_top with this value
    Y_MAX     - Bottom location for enemy (could be off-screen).  Macro will set E_PARAMS.y_max with this value
    X_INC     - Increment value for moving the sprite on X
    Y_INC     - Increment value for moving the sprite on Y
    TIMES     - How many times to move the sprite (using X_INC, Y_INC)
    E_PARAMS  - Parameters to create the sprite
      index_low  - Index for 1st sprite bitmap. Macro will set E_PARAMS.anim_type with this value
      index_high - Index for last sprite bitmap. Macro will set E_PARAMS.anim_type with this value
      anim_type  - Use: ANIM_NONE, ANIM_LOW_HIGH, ANIM_HIGH_LOW or ANIM_PING_PONG
      These where preset perior the MACRO, can be added to the macro as parameters
      x_min      - Leftmost location for enemy (could be off-screen)
      y_min      - Top location for enemy (could be off-screen)
      x_max      - Rightmost location for enemy (could be off-screen)
      y_max      - Bottom location for enemy (could be off-screen)
    ANIMATION_TYPE - Macro will set E_PARAMS.anim_type with this value
    DEST_BITMAP  - Where to draw the sprite
    X_POS        - The starting point for positioning the sprite
    Y_POS        - The starting point for positioning the sprite
    Z_ORD        - Z-ORDER for sprite
    BG_FLAG      - sprite moving on top of Z-ORDER not clearing background
    VISIBLE_FLAG - if the flag is set, show sprite after allocating it.
*/
#define ADD_SPRITE_MENU_ITEM(SPRITE_ITEM,SPRITE_PTR,SPRITE_LIST,SPRITE_INDEX_LOW,SPRITE_INDEX_HIGH,X_MIN,X_MAX,Y_MIN,Y_MAX,X_INC,Y_INC,TIMES,E_PARAMS,ANIMATION_TYPE,DEST_BITMAP,X_POS,Y_POS,Z_ORD,BG_FLAG,VISIBLE_FLAG) \
{ \
SPRITE_ITEM = (sprite_menu_item *) malloc(sizeof(sprite_menu_item)); \
SPRITE_PTR = (SPRITE_LIST)[SPRITE_INDEX_LOW]; \
SPRITE_ITEM->x_inc = X_INC; SPRITE_ITEM->y_inc = Y_INC; SPRITE_ITEM->times = TIMES; \
E_PARAMS.x_min = X_MIN; \
E_PARAMS.x_max = X_MAX; \
E_PARAMS.y_min = Y_MIN; \
E_PARAMS.y_max = Y_MAX; \
E_PARAMS.anim_type = ANIMATION_TYPE; \
E_PARAMS.index_low = SPRITE_INDEX_LOW; \
E_PARAMS.index_high = SPRITE_INDEX_HIGH; \
SPRITE_ITEM->sprite = new class enemy(&(SPRITE_LIST)[0], DEST_BITMAP, X_POS, Y_POS, Z_ORD, BG_FLAG, VISIBLE_FLAG, E_PARAMS); \
s_list.push_back(SPRITE_ITEM); \
}

char *menu::get_controls_text(char *buffer)
{
    /*  Menu keys  */
    char key_name_ESCAPE[16];
    char key_name_SOUND[16];
    char key_name_PAUSE[16];
    char key_name_CHANGE_CONTROLS[16];
    char key_name_SWITCH_GAME[16];
    char key_name_VSYNC[16];
    char key_name_SHIELDS[16];
    char key_name_BULLETS[16];
    char *flag_values[] = { "OFF", "ON" };

    get_key_name(M_KEY_ESCAPE, key_name_ESCAPE);
    get_key_name(M_KEY_SOUND, key_name_SOUND);
    get_key_name(M_KEY_PAUSE, key_name_PAUSE);
    get_key_name(M_KEY_CHANGE_CONTROLS, key_name_CHANGE_CONTROLS);
    get_key_name(M_KEY_SWITCH_GAME, key_name_SWITCH_GAME);
    get_key_name(M_KEY_VSYNC, key_name_VSYNC);
    get_key_name(M_KEY_SHIELDS, key_name_SHIELDS);
    get_key_name(M_KEY_BULLETS, key_name_BULLETS);

    sprintf(buffer, "PAUSE %s\nQUIT %s\n\nSOUND %s (%s)\nBULLETS %s (%d)\nSHIELDS %s (%s)\nVSYNC %s (%s)\n\nSWITCH GAME %s\n", key_name_PAUSE, key_name_ESCAPE, key_name_SOUND, game_names[sound_flag], key_name_BULLETS, (int) pow(2,(GET_BULLETS_BITS)), key_name_SHIELDS, flag_values[GET_SHIELDS], key_name_VSYNC, flag_values[GET_VSYNC], key_name_SWITCH_GAME);

    return buffer;
}

int menu::show_controls(void)
{
    int y_top = 10;
    int menu_key;
    char text[1025];
    text_menu_item *t_m_item;
    class text_msg *controls_text;
    class text_msg *msg;

    char key_name_START_1[16];
    char key_name_START_2[16];

    char key_name_left[16];
    char key_name_right[16];
    char key_name_fire[16];
    
    sprite_menu_item *s_m_item;
    RLE_SPRITE *sp;
    st_enemy_params e_params;

    /*  Show game logo on top  */  
    ADD_SPRITE_MENU_ITEM(s_m_item,sp,*game_sprites->images,(game_sprites->aharon_logo)+GET_GAME_TYPE-1,(game_sprites->aharon_logo)+GET_GAME_TYPE-1,0,SCREEN_W,0,SCREEN_H,0,0,0,e_params,ANIM_NONE,dest,SCREEN_W / 2-((sp->w)/2),y_top,0,G_TRUE,G_FALSE);
    y_top += (int) ((s_m_item->sprite->get_sprite())->h * 1.5);

    /*  Built rest messages off-screen  */
    ADD_TEXT_MENU_ITEM(t_m_item,0,0,0,dest,"PLAYER 1 CONTROLS",SCREEN_W / 4,y_top,game_font,G_TRUE,250,-1,TEXT_CENTER,TEXT_BOTTOM,NULL,G_FALSE);
    ADD_TEXT_MENU_ITEM(t_m_item,0,0,0,dest,"PLAYER 2 CONTROLS",(SCREEN_W / 4)*3,y_top,game_font,G_TRUE,250,-1,TEXT_CENTER,TEXT_BOTTOM,NULL,G_FALSE);
    y_top += (t_m_item->text->get_msg_height());
    get_key_name(player_keys[0].left, key_name_left); get_key_name(player_keys[0].right, key_name_right); get_key_name(player_keys[0].fire, key_name_fire);
    sprintf(text, "LEFT: %s\nRIGHT: %s\nFIRE: %s\n", key_name_left, key_name_right, key_name_fire);
    ADD_TEXT_MENU_ITEM(t_m_item,4,0,SCREEN_W/4,dest,text,-((SCREEN_W / 4)*3),y_top,game_font,G_TRUE,231,-1,TEXT_CENTER,TEXT_BOTTOM,NULL,G_FALSE);

    get_key_name(player_keys[1].left, key_name_left); get_key_name(player_keys[1].right, key_name_right); get_key_name(player_keys[1].fire, key_name_fire);
    sprintf(text, "LEFT: %s\nRIGHT: %s\nFIRE: %s\n", key_name_left, key_name_right, key_name_fire);
    ADD_TEXT_MENU_ITEM(t_m_item,-4,0,SCREEN_W/4,dest,text,SCREEN_W+((SCREEN_W / 4) * 3),y_top,game_font,G_TRUE,231,-1,TEXT_CENTER,TEXT_BOTTOM,NULL,G_FALSE);
    y_top += (t_m_item->text->get_msg_height());
    strcpy(text, "MENU CONTROLS \n");
    ADD_TEXT_MENU_ITEM(t_m_item,0,0,0,dest,text,SCREEN_W / 2,y_top,game_font,G_TRUE,250,-1,TEXT_CENTER,TEXT_BOTTOM,NULL,G_FALSE);

    y_top += (t_m_item->text->get_msg_height());

    get_key_name(M_KEY_START_1, key_name_START_1);
    get_key_name(M_KEY_START_2, key_name_START_2);

    get_controls_text(text);    
    ADD_TEXT_MENU_ITEM(t_m_item,-4,0,SCREEN_W/4,dest,text,SCREEN_W+(SCREEN_W / 2),y_top,game_font,G_TRUE,226,-1,TEXT_CENTER,TEXT_BOTTOM,NULL,G_FALSE);
    controls_text = t_m_item->text;
    y_top += (t_m_item->text->get_msg_height());
    sprintf(text,"PRESS %s OR %s TO START\n", key_name_START_1, key_name_START_2);
    ADD_TEXT_MENU_ITEM(t_m_item,0,0,0,dest,text,SCREEN_W / 2,y_top,game_font,G_TRUE,246,-1,TEXT_CENTER,TEXT_BOTTOM,NULL,G_FALSE);

    /*  Now move them to screen center and play menu */
    menu_key = play_menu((SCREEN_W/4) + 120, controls_text);
    if(!menu_key)
    {
      t_m_item->text->set_blinking_params(24,6);
      menu_key = play_menu(400, controls_text);
    }
    
    clear_menu_items();
    return menu_key;
}

int menu::show_high_score_table(void)
{
    int y_top = 30;
    int menu_key;
    int i, base_index = ((menu_status_register & 3)-1) * 5;
    int half_width_of_names;
    int text_color = 247;
    char text[1024];
    text_menu_item *t_m_item;
    class text_msg *msg;
    
    sprite_menu_item *s_m_item;
    RLE_SPRITE *sp;
    st_enemy_params e_params;

    memset(text, 'W', HIGH_SCORE_NAME_LENGTH+2);
    text[HIGH_SCORE_NAME_LENGTH+2] = '\0';
    half_width_of_names = (text_length((FONT *) s_data[game_font].dat, text)) / 2;
    memset(text, '0', 6);
    text[6] = '\0';
    half_width_of_names += (text_length((FONT *) s_data[game_font].dat, text)) / 2;
    
    /*  Built all messages off-screen  */
    if(menu_status_register & BIT_GALAXIANS)
      y_top = 70;

    sprintf(text, "%s - BEST 5\n\n", game_names[menu_status_register & 3]);
    ADD_TEXT_MENU_ITEM(t_m_item,0,0,0,dest,text,SCREEN_W / 2,y_top,game_font,G_TRUE,250,-1,TEXT_CENTER,TEXT_BOTTOM,NULL,G_FALSE);
    
    y_top += (int) (t_m_item->text->get_msg_height());
    for(int i = base_index; i < base_index+5; i++)
    {
      if(i > base_index)
        text_color = 231;
      strcpy(text, high_scores[i].name);
      ADD_TEXT_MENU_ITEM(t_m_item,4,0,SCREEN_W / 4,dest,text,-(SCREEN_W - ((SCREEN_W / 2) - half_width_of_names)),y_top,game_font,G_TRUE,text_color,-1,TEXT_RIGHT,TEXT_BOTTOM,NULL,G_FALSE);

      sprintf(text,"%d",high_scores[i].score);
      ADD_TEXT_MENU_ITEM(t_m_item,0,0,0,dest,text,((SCREEN_W / 2) + half_width_of_names),y_top,game_font,G_TRUE,text_color,-1,TEXT_LEFT,TEXT_BOTTOM,NULL,G_FALSE);
      
      y_top += (int) (t_m_item->text->get_msg_height() * 1.5);
    }
    
    /*  Now move them to screen center and play menu */
    menu_key = play_menu((SCREEN_W / 4), NULL);
    if(menu_key)
    {
      clear_menu_items(); 
      return menu_key;
    }
    
    /*  Add the Allegro Logo  */
    y_top += (int) (t_m_item->text->get_msg_height() * 2);
    ADD_SPRITE_MENU_ITEM(s_m_item,sp,*game_sprites->images,game_sprites->allegro_logo,game_sprites->allegro_logo,0,SCREEN_W,0,SCREEN_H,0,0,0,e_params,ANIM_NONE,dest,SCREEN_W / 2-((sp->w)/2),y_top,0,G_TRUE,G_FALSE);

    /*  Live the table, with the logo on screen for a while  */
    menu_key = play_menu(200, NULL);
    if(menu_key)
    {
      clear_menu_items(); 
      return menu_key;
    }

    m_msg_list::iterator local_m_iter;
    local_m_iter = m_list.begin();
    local_m_iter++;  // Skip Header.
    while(local_m_iter != m_list.end())
    { /*  Do the flashing of the table entries  */
      (*local_m_iter)->text->set_blinking_params(9,4);
      local_m_iter++;
      (*local_m_iter)->text->set_blinking_params(9,4);
      menu_key = play_menu(80, NULL);
      if(menu_key)
        break;
      
      local_m_iter--;
      (*local_m_iter)->text->set_blinking_params(0,0);
      local_m_iter++;
      (*local_m_iter)->text->set_blinking_params(0,0);
      local_m_iter++;
    }
    
    if(!menu_key)
      menu_key = play_menu(100, NULL);
    
    clear_menu_items(); 
    return menu_key;   
}

#define WINDOW_GET_NAME_WT HIGH_SCORE_NAME_LENGTH+6
#define WINDOW_GET_NAME_HT 5
int menu::update_hi_score_table(char *name, int new_score)
{
    int base_index = ((menu_status_register & 3)-1) * 5;
    int i;
    int midi_sample = HI_SCORE;
    
    for(i = base_index; i < base_index+5; i++)
      if(new_score > high_scores[i].score)
        break;
        
   /*  Not good enough to get to Top-5  */
   if(i == (base_index+5))
     return G_FALSE;

   if(i == (base_index))  //  1st place.
     midi_sample = HI_SCORE_TOP;

    /*  Get name from user  */     
    char temp_t[128];
    memset(temp_t, 0, 128);
    memset(temp_t, ' ', WINDOW_GET_NAME_WT);
    memset(&temp_t[WINDOW_WT], '\n',WINDOW_GET_NAME_HT);
    *(temp_t+WINDOW_GET_NAME_WT+WINDOW_GET_NAME_HT) = '\0';
    text_msg window_bg(dest, temp_t, SCREEN_W / 2, SCREEN_H / 2, GAL_FONT, G_TRUE, 246, -1, TEXT_CENTER, TEXT_CENTER, (*game_sprites).game_logo, G_FALSE);
    sprintf(temp_t, " %s YOU ARE IN THE TOP FIVE\n ENTER YOUR NAME: ", name);
    text_msg window_header(dest, temp_t, SCREEN_W / 2, (window_bg.get_y_pos() - (window_bg.get_msg_height() / 2)+10), MENU_FONT, G_TRUE, 225, -1, TEXT_CENTER, TEXT_BOTTOM, NULL, G_FALSE);
    *temp_t = 0; // 0x7F;

    text_msg window_text(dest, temp_t, window_bg.get_x_pos() - (window_bg.get_msg_width() / 2) + 20, (window_bg.get_y_pos() - (window_bg.get_msg_height() / 2)+90), MENU_FONT, G_TRUE, 229, -1, TEXT_RIGHT, TEXT_BOTTOM, NULL, G_FALSE);
    
    int frame_computed;
    unsigned int frames = 0;
    unsigned int c_frames = 0;
    int caret = 0;
    int  newkey;
    char ASCII;
    char scancode;
    char *str_tm;
    time_t lt;

   
    window_bg.draw();
    window_header.draw();
    clear_keybuf();
    
    if(sound_flag)
      play_midi((MIDI *) s_data[midi_sample].dat, 0);
    do
    {
       if(!frame_computed)
       {
         if(stars_layer_1)
         {
          stars_layer_1->remove();
          stars_layer_1->move();
         }

         window_text.remove();
 
         if(keypressed())
         {
           /* a character key was pressed; add it to the string */
           newkey   = readkey();
           ASCII    = newkey & 0xff;
           scancode = newkey >> 8;
           if(ASCII >= 32 && ASCII <= 126)
             if(strlen(temp_t) < HIGH_SCORE_NAME_LENGTH)
             {
                temp_t[caret] = toupper(ASCII);
                caret++;
             }
          
           if(scancode == KEY_BACKSPACE)
           {
              if(caret)
                caret--;
           }
         }
      
        if(c_frames & 1)  //  animate cursor.
          temp_t[caret] = 0x7f;
        else
          temp_t[caret] = 0x20;
        
        temp_t[caret+1] = '\0';  
        
        window_text.set_text(temp_t);
        window_text.draw();

        if(stars_layer_1)
          stars_layer_1->draw();

        frame_computed = G_TRUE;
        }
                    
       /* all drawing goes here */
       if(time_for_frame) // if(time_for_frame)
       {
         frames++;
         if(!(frames % 15))
           c_frames++;  //  Cursor will flash every 16 frames.
           
         acquire_screen();
         if(menu_status_register & BIT_VSYNC)
           vsync();
         blit(dest, screen, 0, 0, 0, 0 , SCREEN_W, SCREEN_H);
         release_screen();
        
         time_for_frame = G_FALSE;
         frame_computed = G_FALSE;
        }
    }while((frames < 1500) && (!key[KEY_ESC])&& (!key[KEY_ENTER]));
    while((key[KEY_ESC]) || (key[KEY_ENTER]))  //  Wait for release.
      poll_keyboard();

    if(sound_flag)
      stop_midi();

    window_text.remove();
    window_header.remove();
    window_bg.remove();
   
    temp_t[caret] = '\0';  //  Clear cursor char.
    if(!caret)
    { //  Name is empty, copy current-time instead.     
      lt = time(NULL);
      str_tm = ctime(&lt);
      strncpy(temp_t, str_tm, 16);
      temp_t[16] = '\0';
    }

    high_scores[i].score = new_score;
    strcpy(high_scores[i].name, temp_t);
    return G_TRUE;     
}

int menu::show_scores_table(void)
{
    int i,y_top = 120;
    int y_top_inc;
    int inv_rows = INVADERS_ROWS;
    int menu_key;
    char text[1024];
    text_menu_item *t_m_item;
    sprite_menu_item *s_m_item;
    RLE_SPRITE *sp;
    class text_msg *msg;
    st_enemy_params e_params;

    if((menu_status_register & 3) == 3)
    {
      inv_rows = INV_GLAVADERS_ROWS;
      y_top = 30;
    }
      
    if(menu_status_register & BIT_INVADERS)
    { /*  Show game name at header  */
      ADD_TEXT_MENU_ITEM(t_m_item,0,0,0,dest,game_names[1],SCREEN_W / 2,y_top,INV_FONT,G_TRUE,250,-1,TEXT_CENTER,TEXT_BOTTOM,NULL,G_FALSE);
      y_top_inc = (t_m_item->text->get_msg_height());
      y_top += (y_top_inc * 2);

      /*  Built rest messages off-screen  */
      for(i = 0 ; i < inv_rows ; i++)
      {
        ADD_SPRITE_MENU_ITEM(s_m_item,sp,*game_sprites->images,game_sprites->enemies+(i << 1),e_params.index_low+1,-SCREEN_W,SCREEN_W*2,-SCREEN_H,SCREEN_H*2,-4,0,SCREEN_W / 4,e_params,ANIM_LOW_HIGH,dest,SCREEN_W+(SCREEN_W / 4),y_top,0,G_TRUE,G_FALSE);

        sprintf(text, "INVADER ROW <%d> %d", i+1, (i+1)*5);
        ADD_TEXT_MENU_ITEM(t_m_item,-4,0,SCREEN_W / 4,dest,text,SCREEN_W+(SCREEN_W / 4)+(sp->w),y_top,INV_FONT,G_TRUE,230,-1,TEXT_RIGHT,TEXT_BOTTOM,NULL,G_FALSE);
        
        if(sp->h > y_top_inc)
          y_top_inc = sp->h;
          
       y_top += (int) (y_top_inc * 1.5);
      }      

      ADD_SPRITE_MENU_ITEM(s_m_item,sp,*game_sprites->images,game_sprites->inv_mother_ship,e_params.index_low+3,-SCREEN_W,SCREEN_W*2,-SCREEN_H,SCREEN_H*2,-4,0,SCREEN_W / 4,e_params,ANIM_LOW_HIGH,dest,SCREEN_W+(SCREEN_W / 8),y_top,0,G_TRUE,G_FALSE);

      sprintf(text, "MOTHER SHIP 150, 300 FOR CENTER HIT");
      ADD_TEXT_MENU_ITEM(t_m_item,-4,0,SCREEN_W / 4,dest,text,SCREEN_W+(SCREEN_W / 8)+(sp->w),y_top,INV_FONT,G_TRUE,230,-1,TEXT_RIGHT,TEXT_BOTTOM,NULL,G_FALSE);
      
      y_top += y_top_inc * 2;
    }

    if(menu_status_register & BIT_GALAXIANS)
    { /*  Show game name at header  */
      ADD_TEXT_MENU_ITEM(t_m_item,0,0,0,dest,game_names[2],SCREEN_W / 2,y_top,GAL_FONT,G_TRUE,250,-1,TEXT_CENTER,TEXT_BOTTOM,NULL,G_FALSE);
      y_top_inc = (t_m_item->text->get_msg_height());
      y_top += (y_top_inc * 2);
      
      /*  Built rest messages off-screen  */
      for(i = 0 ; i < 4 ; i++)
      {
        ADD_SPRITE_MENU_ITEM(s_m_item,sp,*game_sprites->images,game_sprites->enemies+GALAXY_FIRST_RAW_SPRITE+(i * GALAXY_INC_ROW_SPRITE),e_params.index_low+1,-SCREEN_W,SCREEN_W*2,-SCREEN_H,SCREEN_H*2,4,0,SCREEN_W / 4,e_params,ANIM_LOW_HIGH,dest,-((SCREEN_W / 4)*3),y_top,0,G_TRUE,G_FALSE);
      
        if(i)
          sprintf(text, "GALAXIAN %d %d", (5-i)*10, (5-i)*20);
        else
          strcpy(text, "GALAXIAN 100");

        ADD_TEXT_MENU_ITEM(t_m_item,4,0,SCREEN_W / 4,dest,text,-(((SCREEN_W / 4)*3)-(sp->w)),y_top,GAL_FONT,G_TRUE,230,-1,TEXT_RIGHT,TEXT_BOTTOM,NULL,G_FALSE);
        
        if(!i)  //  Bonus points sprite.
          ADD_SPRITE_MENU_ITEM(s_m_item,sp,*game_sprites->images,game_sprites->gal_bonus_200,e_params.index_low+11,-SCREEN_W,SCREEN_W*2,-SCREEN_H,SCREEN_H*2,4,0,SCREEN_W / 4,e_params,ANIM_LOW_HIGH,dest,-((SCREEN_W / 4)*3)+(t_m_item->text->get_msg_width()+40),y_top+((t_m_item->text->get_msg_height())/4) ,0,G_TRUE,G_FALSE);

        if(sp->h > y_top_inc)
          y_top_inc = sp->h;

       y_top += (int) (y_top_inc * 1.5);
      }      
    }
    
    /*  Now move them to screen center and play menu */
    menu_key = play_menu((SCREEN_W / 4) + 200, NULL);
                
    clear_menu_items(); 
    return menu_key;   
    
}

#define DRAW_GAME_HEADER { \
    high_score_header.draw(); \
    player_1->draw_player_text(menu_status_register & BIT_GAME_OVER); \
    high_score_text.draw(); \
    player_2->draw_player_text(menu_status_register & BIT_GAME_OVER); \
    if(!(menu_status_register & BIT_GALAXIANS)) \
      rectfill(dest, 0, GAME_HEADER_BORDER_LINE, SCREEN_W, GAME_HEADER_BORDER_LINE+1, 130); }

#define REMOVE_GAME_HEADER { \
    high_score_header.remove(); \
    player_1->remove_player_text(); \
    high_score_text.remove(); \
    player_2->remove_player_text(); \
    if(!(menu_status_register & BIT_GALAXIANS)) \
      rectfill(dest, 0, GAME_HEADER_BORDER_LINE, SCREEN_W, GAME_HEADER_BORDER_LINE+1, 0); }

/*  Show player extra-lives only in game mode  */
#define DRAW_GAME_FOOTER { \
    footer.draw(); \
    if(!(menu_status_register & BIT_DEMO_MODE)) \
    { \
      player_1->draw_player_lives(); \
      player_2->draw_player_lives(); \
    } \
    if(menu_status_register & BIT_INVADERS) \
      rectfill(game_footer, 0, 0, SCREEN_W, 1, 130); }

#define REMOVE_GAME_FOOTER { \
    footer.remove(); \
    player_1->remove_player_lives(); \
    player_2->remove_player_lives(); \
    if(menu_status_register & BIT_INVADERS) \
      rectfill(game_footer, 0, 0, SCREEN_W, 1, 0); }

#define TEST_FOR_JOIN(PLAYER,PLAYER_TO_SYNC,KEY_TO_TEST,KEY_PRESSED) { \
         if(KEY_PRESSED == KEY_TO_TEST) \
         { \
           KEY_PRESSED = 0; \
           if(!(PLAYER->get_lives())) \
           { \
             /* menu_status_register &= CLEAR_GAME_OVER; */ \
             /* game_over_counter = 120;  */ \
             PLAYER->switch_to_regular_sprite(); \
             PLAYER->set_lives(3); \
             PLAYER->draw_player_lives(); \
             if(PLAYER_TO_SYNC->get_lives()) \
             { \
               PLAYER->switch_to_halftone_sprite(200); \
               PLAYER_TO_SYNC->sync_blink_of_score_header(); \
             } \
           } \
         } \
}

#define TEST_PLAYER_HIGH_SCORE(PLAYER) { \
         if(PLAYER->get_score() > high_score) \
         { \
           high_score = PLAYER->get_score(); \
           make_high_score(high_score, text_h_score); \
           high_score_text.set_text(text_h_score); \
         } \
}

#define SHIELD_Y_TOP 364
void menu::do_shields(void)
{
    int shield_x_step;
    int i;
    RLE_SPRITE *shield_sprite = (*game_sprites->images)[game_sprites->shield];
    shield_x_step = (SCREEN_W - (3 * shield_sprite->w)) / 3;
    for(i = 1; i <=3; i++)
      draw_rle_sprite(dest, shield_sprite, i*shield_x_step, SHIELD_Y_TOP);
}

int menu::pause_game(void)
{
    int menu_key = 0;
    BITMAP *last_frame;
    set_palette((RGB *) s_data[GAMP_PAUSE_P].dat);
    text_msg pause_text(dest, "GAME PAUSED", SCREEN_W / 2, SCREEN_H / 2, GAL_FONT, G_TRUE, 245, 215, TEXT_CENTER, TEXT_CENTER, NULL, G_TRUE);

    menu_state = STATE_PAUSE_GAME;
    
    pause_after_get_window_focus = 0;
    last_frame = create_bitmap(SCREEN_W, SCREEN_H);

    acquire_screen();
    blit(dest, screen, pause_text.get_x_pos()-(pause_text.get_msg_width()/2), pause_text.get_y_pos()-(pause_text.get_msg_height()/2), pause_text.get_x_pos()-(pause_text.get_msg_width()/2), pause_text.get_y_pos()-(pause_text.get_msg_height()/2) , pause_text.get_msg_width(), pause_text.get_msg_height());
    if(last_frame)  //  This will be used when getting focus back in pause-mode
      blit(screen, last_frame, 0, 0, 0, 0 , SCREEN_W, SCREEN_H);
    release_screen();
          
    stop_sound(0);  //  Stop all sound.
    
    while(!menu_key)
    { 
      if(pause_after_get_window_focus & REPAINT_WINDOW_AFTER_GET_FOCUS)
      { //  Got focus in pause-modem need to repaint last frame.
        pause_after_get_window_focus = 0;
        acquire_screen();
        if(last_frame)  //  If managed to save last frame, blit to screen.
          blit(last_frame, screen, 0, 0, 0, 0 , SCREEN_W, SCREEN_H);
        else            //  Did not save last frame., Just show the pause-text message.
          blit(dest, screen, pause_text.get_x_pos()-(pause_text.get_msg_width()/2), pause_text.get_y_pos()-(pause_text.get_msg_height()/2), pause_text.get_x_pos()-(pause_text.get_msg_width()/2), pause_text.get_y_pos()-(pause_text.get_msg_height()/2) , pause_text.get_msg_width(), pause_text.get_msg_height());
        release_screen();
      }
      
      menu_key = test_menu_keys();
      if(menu_key == M_KEY_PAUSE)
      {
        menu_key = 0;
        break;
      }
      
      if(player_1->get_lives())
      {
        if(key[player_keys[0].right])
          break;
        if(key[player_keys[0].left])
          break;
        if(key[player_keys[0].fire])
          break;
      }
      
      if(player_2->get_lives())
      {
        if(key[player_keys[1].right])
          break;
        if(key[player_keys[1].left])
          break;
        if(key[player_keys[1].fire])
         break;
      }
    }
    
    if(last_frame)
      destroy_bitmap(last_frame);
      
    pause_text.remove();
    set_palette((RGB *) s_data[GAME_P].dat);
    menu_state = STATE_PLAY_THE_GAME;
    return menu_key;
}

void menu::remove_shields(void)
{
    int shield_x_step;
    int i;
    RLE_SPRITE *shield_sprite = (*game_sprites->images)[game_sprites->shield];
    shield_x_step = (SCREEN_W - (3 * shield_sprite->w)) / 3;
    for(i = 1; i <=3; i++)
      rectfill(dest, i*shield_x_step, SHIELD_Y_TOP, (i*shield_x_step)+shield_sprite->w, SHIELD_Y_TOP+shield_sprite->h, 0);
}


int menu::handle_menu_input(void)
{  /*  This function returns ZERO if menu-key was handled, otherwise return menu_key value  */
    int menu_key;
    unsigned char flag;
    unsigned int old_sound_flag = sound_flag;
    
    /*  Check menu-keys  */
    menu_key = test_menu_keys();
    if(menu_status_register & BIT_DEMO_MODE)
      return menu_key;  //  In demo-mode don't handle menu input. Will be handled after exit from demo.
      
    //  Code for Pause here
    if(menu_key == M_KEY_PAUSE)
      menu_key = pause_game();
         
    if(!(menu_status_register & BIT_GAME_OVER))
    { /*  TEST FOR JOIN  */
      TEST_FOR_JOIN(player_1,player_2,M_KEY_START_1,menu_key);
      TEST_FOR_JOIN(player_2,player_1,M_KEY_START_2,menu_key);
    }
         
    switch(menu_key)
    {
      case M_KEY_SOUND:
        sound_flag = (sound_flag + sound_inc) & sound_expr;
        menu_key = 0;
        stop_sound(old_sound_flag);
        break;

      case M_KEY_SHIELDS:
        menu_status_register ^= BIT_SHIELDS;
        menu_key = 0;
        break;

      case M_KEY_VSYNC:
        menu_status_register ^= BIT_VSYNC;
        menu_key = 0;
        break;

      case M_KEY_CHANGE_CONTROLS:
        stop_sound(0);  //  First stop all sound.
        flag = change_controls();
        if(flag)
        { //  Do the actual change if user did not regrate.
          player_1->set_controls(player_keys[0]);
          player_2->set_controls(player_keys[1]);
        }
        menu_key = 0;
        break;
        
      case M_KEY_BULLETS:
        flag = GET_BULLETS_BITS;
        if(flag < 3)
          flag++;
        else
          flag = 0;
            
        SET_BULLETS_BITS(flag);
        player_1->set_max_bullets((int) pow(2,(GET_BULLETS_BITS)));
        player_2->set_max_bullets((int) pow(2,(GET_BULLETS_BITS)));
        menu_key = 0;
        break;
    }
         
         return menu_key;
}

void menu::record_player_keys(unsigned short n_player, unsigned short *player_data, unsigned short *player_data_index)
{
    char key_index;
    unsigned short player_keys_state = 0;
    unsigned char *p_keys = (unsigned char *) &player_keys[n_player];
    for(key_index = 0; key_index <= 2; key_index++)
      if(key[p_keys[key_index]])
        player_keys_state |= 1 << key_index;

    if((*player_data_index) == MAX_PLAYER_DATA)
      return;
      
    if(GET_FRAME_COUNTER(player_data[*player_data_index]) == 0x1FFF)
      (*player_data_index)++;
           
    if((player_data[*player_data_index] >> 13)== player_keys_state)
      player_data[*player_data_index]++;
    else
    {
      (*player_data_index)++;
      SET_FRAME_KEYS(player_keys_state,1,player_data[*player_data_index]);
    }
}

int menu::play_the_game(int n_players)
{
    int player_sprite_index;
    int small_player_sprite_index;
    int frame_computed;
    int game_over_counter = 200;
    int game_over_blink_on;
    int game_over_blink_off;
    int stage = 1;
    int game_over_color;
    int menu_key;
    int stage_header_counter = 0;
    int high_score = high_scores[((menu_status_register & 3)-1) * 5].score;
    
    /*  BONUS LIVE defs:     SPACE INVADERS                   GALAXIANS                            GALVADERS  */
    int player_bonuses[] = { 1000, 3000, 7000, 10000,  4000, 10000, 30000, 70000,  1500, 4000, 7000, 10000 };

    char player_1_header[12];
    char player_2_header[12];
    char text_h_score[7];
    char text_s_h[8];
    char text_stage_header[12];


    /*  Iterators  */ 
    sprite_list::iterator iter;
    sprite_list::iterator to_delete;
    attack_list::iterator iter_gal_attacks;
    attack_list::iterator attack_to_delete;
    
    st_player_params p;

    clear_bitmap(dest);
    init_classes_static_data(GET_GAME_TYPE);

    invaders = NULL;
    galaxians = NULL;

    menu_status_register &= CLEAR_GAME_OVER;
    formation::set_attack_ok();
    
    game_over_blink_on = game_over_blink_off = 0;
    strcpy(player_1_header, "PLAYER <1>");
    strcpy(player_2_header, "PLAYER <2>");
    strcpy(text_s_h, "LEVEL");

    player_sprite_index = game_sprites->gal_players;
    p.halftone_sprite_index = game_sprites->halftone_gal_players;
    p.exp_sprite_index_low = game_sprites->gal_player_blast;
    p.exp_sprite_index_high = game_sprites->gal_player_blast+2;
    p.bullet_sprite_index = game_sprites->gal_player_bullet;
    
    p.player_hit_pixels = player::gal_player_hit_pixles;
    p.n_player_hit_pixels = 7;
    
    p.player_bonus_scores = &player_bonuses[((GET_GAME_TYPE) - 1) * PLAYER_BONUSES];
    
    p.fire_sample = S_GALAXY_FIRE;
    p.exp_sample = S_GAL_PLAYER_EXP;
    p.bonus_life_sample = S_BONUS_LIFE;
    small_player_sprite_index = game_sprites->gal_players_small;
    if(menu_status_register & BIT_INVADERS)
    {
      game_over_color = 215;
      p.fire_sample = S_INV_FIRE;
      p.exp_sample = S_INV_PLAYER_EXP;
      p.exp_sprite_index_low = game_sprites->inv_player_blast;
      p.exp_sprite_index_high = game_sprites->inv_player_blast+2;
      player_sprite_index = game_sprites->inv_players;
      p.halftone_sprite_index = game_sprites->halftone_inv_players;
      p.bullet_sprite_index = game_sprites->inv_player_bullet;
      p.player_hit_pixels = player::inv_player_hit_pixles;
      p.n_player_hit_pixels = 6;
      small_player_sprite_index = game_sprites->inv_players_small;
    }
    if(menu_status_register & BIT_GALAXIANS)
    {
      game_over_color = 246;
      game_over_blink_on = 18;
      game_over_blink_off = 6;
      strcpy(player_1_header, "1UP");
      strcpy(player_2_header, "2UP");
      strcpy(text_s_h, "STAGE");
    }

    make_high_score(high_score, text_h_score);
    class text_msg footer(game_footer, game_names[menu_status_register & 3], SCREEN_W /2, (game_footer->h)-(text_height((FONT *) s_data[GAL_FONT].dat)), game_font, G_FALSE, 251, -1, TEXT_CENTER, TEXT_BOTTOM, NULL, G_FALSE);
    class text_msg high_score_header(dest, "HIGH", SCREEN_W / 2, 0, game_font, G_TRUE, 246, -1, TEXT_CENTER, TEXT_BOTTOM, NULL, G_FALSE);
    class text_msg high_score_text(dest, text_h_score, SCREEN_W / 2, text_height((FONT *) s_data[game_font].dat), game_font, G_TRUE, 215, -1, TEXT_CENTER, TEXT_BOTTOM, NULL, G_FALSE);
    class text_msg game_over_text(dest, "GAME OVER", SCREEN_W / 2, SCREEN_H / 2, game_font, G_TRUE, game_over_color, -1, TEXT_CENTER, TEXT_CENTER, NULL, game_over_blink_on, game_over_blink_off, G_FALSE);
    class text_msg stage_text(dest, "BEGIN", SCREEN_W / 2, SCREEN_H / 3, game_font, G_TRUE, 250, -1, TEXT_CENTER, TEXT_CENTER, NULL, G_FALSE);
    
    p.x_min = 0;
    p.x_max = SCREEN_W - (*game_sprites->images)[player_sprite_index]->w;
    p.x_inc = 4;
    p.bullets_max = (int) pow(2,(GET_BULLETS_BITS));
    p.keys = &player_keys[0];

    p.demo_stream = NULL;
    if(menu_status_register & BIT_DEMO_MODE)
      p.demo_stream = game_demo[GET_GAME_TYPE-1];
      
    player_1 = new player(&(*game_sprites->images)[0], dest, game_footer, player_sprite_index, p.x_min, GAME_FOOTER_BORDER_LINE-((*game_sprites->images)[player_sprite_index]->h),  PLAYER_Z_ORDER, 3, G_TRUE, G_FALSE, p, game_font, player_1_header, 0, 0, TEXT_RIGHT, TEXT_BOTTOM, "000000", 0, text_height((FONT *) s_data[game_font].dat), TEXT_RIGHT, TEXT_BOTTOM, "PRESS 1", 0, game_footer->h, TEXT_RIGHT, TEXT_TOP, small_player_sprite_index);
    p.halftone_sprite_index++;
    p.bullet_sprite_index++;
    small_player_sprite_index++;
    player_sprite_index++;
    
    p.keys = &player_keys[1];

    p.demo_stream = NULL;  //  No demo for two-players.
    
    if(n_players > 1)
      player_2 = new player(&(*game_sprites->images)[0], dest, game_footer, player_sprite_index, p.x_max, GAME_FOOTER_BORDER_LINE-((*game_sprites->images)[player_sprite_index]->h),  PLAYER_Z_ORDER+1, 3, G_TRUE, G_FALSE, p, game_font, player_2_header, SCREEN_W, 0, TEXT_LEFT, TEXT_BOTTOM, "000000", SCREEN_W, text_height((FONT *) s_data[game_font].dat), TEXT_LEFT, TEXT_BOTTOM, "PRESS 2", SCREEN_W, game_footer->h, TEXT_LEFT, TEXT_TOP, small_player_sprite_index);
    else //  Prepare player object with Zero lives.
      player_2 = new player(&(*game_sprites->images)[0], dest, game_footer, player_sprite_index, p.x_max, GAME_FOOTER_BORDER_LINE-((*game_sprites->images)[player_sprite_index]->h),  PLAYER_Z_ORDER+1, 0, G_TRUE, G_FALSE, p, game_font, player_2_header, SCREEN_W, 0, TEXT_LEFT, TEXT_BOTTOM, "000000", SCREEN_W, text_height((FONT *) s_data[game_font].dat), TEXT_LEFT, TEXT_BOTTOM, "PRESS 2", SCREEN_W, game_footer->h, TEXT_LEFT, TEXT_TOP, small_player_sprite_index);


    if(stars_layer_1)
    {
       stars_layer_1->remove();
       stars_layer_1->move();
    }
    
    if(menu_status_register & BIT_SHIELDS)
      do_shields();

    DRAW_GAME_FOOTER;
    
    //  Start_level.
    if(menu_status_register & BIT_INVADERS)
      invaders = create_invaders_formation(menu_status_register & 3, stage, &(*game_sprites->images)[0], game_sprites->enemies, dest);
    if(menu_status_register & BIT_GALAXIANS)
      galaxians = create_galaxian_formation(menu_status_register & 3, stage, &(*game_sprites->images)[0], game_sprites->enemies+GALAXY_FIRST_RAW_SPRITE, dest);

    srand(time(0));
    frame_computed = G_FALSE;
    
// allegro_message("FRAME TIME");
//#define RECORDING_GAME
#ifdef RECORDING_GAME
    unsigned short player_1_data[MAX_PLAYER_DATA+1];
    unsigned short player_1_data_index = 1;
    memset(player_1_data, 0, MAX_PLAYER_DATA+1);
    char *file_name;
    FILE *fp;
#endif

    menu_key = 0;
    while (!menu_key)
    {       
       if(!frame_computed)
       {
         //  First remove items from screen in by reverse Z-ORDER
         //  Then do items action() and redraw on be Z-ORDER.         
         if(stars_layer_1)
         {
           stars_layer_1->remove();
           stars_layer_1->move();
         }
         
         if(menu_status_register & (BIT_GAME_OVER | BIT_DEMO_MODE))
           game_over_text.remove();
         if(stage_header_counter)  
           stage_text.remove();
           
         REMOVE_GAME_HEADER;
         RUN_LIST_REVERSE(explosions_list,iter,remove);
         RUN_LIST_REVERSE(p_bullets_list,iter,remove);
         RUN_LIST_REVERSE(e_bullets_list,iter,remove);
         
         
         if(menu_status_register & BIT_GALAXIANS)
         { //  Test if player was hit by an attacking galaxian
           if(player_1->get_lives())
             player_1->p_test_hit_pixels();
           if(player_2->get_lives())
             player_2->p_test_hit_pixels();
         }
           
         RUN_LIST_REVERSE(gal_attack_list,iter_gal_attacks,remove);
         player_2->remove();
         player_1->remove();                        

         if(menu_status_register & BIT_GALAXIANS)
           galaxians->remove();
           
         /*  Lowest in the Z-ORDER  */
         if(menu_status_register & BIT_INVADERS)
           invaders->remove();

         /*  START - Do action for all items on scrren. (from High-Z-order to low) */
         if(player_2->get_player_killed_by_enemy())
           player_2->kill(player_1->get_lives());
         if(player_1->get_player_killed_by_enemy())
           player_1->kill(player_2->get_lives());

         RUN_LIST_INCREMENTAL_WITH_REMOVE(explosions_list,iter,to_delete,action,get_sprite_index);
         RUN_LIST_INCREMENTAL_WITH_REMOVE(p_bullets_list,iter,to_delete,action,get_sprite_index);
         RUN_LIST_INCREMENTAL_WITH_REMOVE(e_bullets_list,iter,to_delete,action,get_sprite_index);
         RUN_LIST_INCREMENTAL_WITH_REMOVE(gal_attack_list,iter_gal_attacks,attack_to_delete,do_attack,get_attack_mode);
           
         player_2->action();
         player_1->action();
         
         if(menu_status_register & BIT_GALAXIANS)
           galaxians->action();
         if(menu_status_register & BIT_INVADERS)
         {
           invaders->action();
           if(invaders->get_invaders_landed_on_earth())
           { /*  Invaders lannded on Earth - end game.  */
             player_1->remove_player_lives();
             player_2->remove_player_lives();
             if(player_2->get_lives())
             {
               player_2->set_lives(1);
               player_2->kill(0);
             }
             if(player_1->get_lives())
             {
               player_1->set_lives(1);
               player_1->kill(0);
             }
             
             menu_status_register |= BIT_GAME_OVER;
           }
         }
         
         if(menu_status_register & BIT_DEMO_MODE)
           if(player_1->end_of_demo_input())
             break;
         /*  END   - Do action for all items  */
         
#ifdef RECORDING_GAME
         record_player_keys(0, player_1_data, &player_1_data_index);
#endif

         /*  Test if need to update High-Score  */
         TEST_PLAYER_HIGH_SCORE(player_1);
         TEST_PLAYER_HIGH_SCORE(player_2);

         if(stage_header_counter)
         { //  Show new stage message.
           stage_header_counter--;
           if(!stage_header_counter)
           { //  Start new stage after text disappears.
             if(menu_status_register & BIT_SHIELDS)
               do_shields();

             if(menu_status_register & BIT_INVADERS)
             {
               delete invaders;
               invaders = create_invaders_formation(menu_status_register & 3, stage, &(*game_sprites->images)[0], game_sprites->enemies, dest);
             }  

             if(menu_status_register & BIT_GALAXIANS)
             {
                delete galaxians;
                galaxians = create_galaxian_formation(menu_status_register & 3, stage, &(*game_sprites->images)[0], game_sprites->enemies+GALAXY_FIRST_RAW_SPRITE, dest);
             }
           }
         }

         
         /*  Test if level is clear from enemies.  */
         if(!enemy::get_enemies())
           formation::clear_attack_ok();  //  Stop all attack.

         menu_key = handle_menu_input();
         if(menu_key)
           break;   /*  If keypressed was not habdled, exit from game loop  */
                    
         /*  TEST GAME STATE  */
         if(!(formation::get_attack_ok()))
         {  //  If attack-bit clear: Test if game-over, player killed or next stage.
            if(menu_status_register & BIT_GAME_OVER)
            { //  Show GAME OVER message.
              game_over_counter--;
              if(!game_over_counter)
                break;  //  Exit game loop after showing GAME-OVER message
            }
                     
            if(!(explosions_list.size() | p_bullets_list.size() | e_bullets_list.size() | gal_attack_list.size()))
            { //  Wait for all lists to be cleared. Test for Game-Over/Next-stage.
              if(menu_status_register & BIT_DEMO_MODE)
                break;  //  Exit if killed on demo mode.
                
              if(player_1->get_lives())
              { //  New player shows on screen now, update lives.
                player_1->remove_player_lives();
                player_1->clear_killed_flag();
                player_1->draw_player_lives();
              }

              if(player_2->get_lives())
              { //  New player shows on screen now, update lives.
                player_2->remove_player_lives();
                player_2->clear_killed_flag();
                player_2->draw_player_lives();
              }
              
              if(player_1->get_lives() + player_2->get_lives())
              { //  Player still lives, show next-stage message.
                if(!enemy::get_enemies())
                  if(!stage_header_counter)
                  { //  Trigger next stage.
                    stage++;
                    sprintf(text_stage_header, "%s %d", text_s_h, stage);
                    stage_text.set_text(text_stage_header);
                    stage_header_counter = 120;
                  }
                  
                formation::set_attack_ok();  //  Enable attack.
                if(invaders)  //  Let player get ready before Invaders start shooting.
                  invaders->set_next_bullet(180);

              }
              else
              { //  Game Over.
                menu_status_register |= BIT_GAME_OVER;
              }
            }
         }  /* END - TEST GAME STATE  */
         
         
         if(menu_status_register & BIT_DEMO_MODE)
         { //  display recent score in demo-mode.
           player_1->set_score(player_1_last_game_score);
           player_2->set_score(player_2_last_game_score);
         }
         
         /*  Draw back on screen  */
         if(menu_status_register & BIT_INVADERS)
           invaders->draw();
         if(menu_status_register & BIT_GALAXIANS)
           galaxians->draw();
         
         player_1->draw();
         player_2->draw();
         
         RUN_LIST_INCREMENTAL(gal_attack_list,iter_gal_attacks,draw);
         RUN_LIST_INCREMENTAL(e_bullets_list,iter,draw);
         RUN_LIST_INCREMENTAL(p_bullets_list,iter,draw);
         RUN_LIST_INCREMENTAL(explosions_list,iter,draw);
         DRAW_GAME_HEADER;
         
         if(stage_header_counter)  
           stage_text.draw();
         if(menu_status_register & (BIT_GAME_OVER | BIT_DEMO_MODE))
           game_over_text.draw();
         
         if(stars_layer_1)
           stars_layer_1->draw();

         frame_computed = G_TRUE;
       }  //  end if(!frame_computed...

       /*****   START  --  SAVE SCREENSHOT  ****
       BITMAP *last_frame;
       
       char screenshot_file_name[128];
       sprintf(screenshot_file_name, "%s%s_%ld.bmp", (*game_sprites).dir, game_names[menu_status_register & 3], time(NULL));
       if(key[KEY_F12])
       {
         last_frame = create_bitmap(SCREEN_W, SCREEN_H);
         acquire_screen();
         if(last_frame)  //  This will be used when getting focus back in pause-mode
           blit(screen, last_frame, 0, 0, 0, 0 , SCREEN_W, SCREEN_H);
         release_screen();

         save_bitmap(screenshot_file_name, last_frame, (RGB *) s_data[GAME_P].dat);
         if(last_frame)
           destroy_bitmap(last_frame);
       }
        *****   END    --  SAVE SCREENSHOT  */

       if(time_for_frame) // if(time_for_frame)
       {
         acquire_screen();
         if(menu_status_register & BIT_VSYNC)
           vsync();
         blit(dest, screen, 0, 0, 0, 0 , SCREEN_W, GAME_FOOTER_BORDER_LINE);
         blit(game_footer, screen, 0, 0, 0, GAME_FOOTER_BORDER_LINE, SCREEN_W, SCREEN_H-GAME_FOOTER_BORDER_LINE);
         release_screen();

         time_for_frame = G_FALSE;
         frame_computed = G_FALSE;
       }
    }
//  ------  GAME END   -----
    stop_sound(0);  //  Stop all sound.

#ifdef RECORDING_GAME
    player_1_data[0] = player_1_data_index;
    
    file_name = (char *) malloc((strlen((*game_sprites).dir)) + (strlen(GALVADERS_DEMO_FILE))+1);
    sprintf(file_name,"%s%s",(*game_sprites).dir, GALVADERS_DEMO_FILE); 
/*
    file_name = (char *) malloc((strlen((*game_sprites).dir)) + (strlen(INVADERS_DEMO_FILE))+1);
    sprintf(file_name,"%s%s",(*game_sprites).dir, INVADERS_DEMO_FILE); 
    file_name = (char *) malloc((strlen((*game_sprites).dir)) + (strlen(GALAXY_DEMO_FILE))+1);
    sprintf(file_name,"%s%s",(*game_sprites).dir, GALAXY_DEMO_FILE); 
*/

    if((fp = fopen(file_name, "wb")) != NULL)
    {
      fwrite(player_1_data, sizeof(short), player_1_data_index, fp);
      fclose(fp);
    }
    free(file_name);
#endif


    remove_shields();    
    REMOVE_GAME_FOOTER;
    if((!(menu_status_register & BIT_DEMO_MODE)) && (menu_key != M_KEY_SWITCH_GAME))
    { /*  Save scores to display in demo-mode, Update High-Score (take no action if player reset-game)  */
      player_1_last_game_score = player_1->get_score();
      player_2_last_game_score = player_2->get_score();
      if(player_1->get_score() >= player_2->get_score())
      { //  Player ONE gets to high-score table first.
        update_hi_score_table("PLAYER ONE", player_1->get_score());
        update_hi_score_table("PLAYER TWO", player_2->get_score());
      }
      else
      { //  Player TWO gets to high-score table first.
        update_hi_score_table("PLAYER TWO", player_2->get_score());
        update_hi_score_table("PLAYER ONE", player_1->get_score());
      }
    }
    
    //  Cleanup.
    CLEAR_LIST(explosions_list,iter,to_delete);
    CLEAR_LIST(p_bullets_list,iter,to_delete);
    CLEAR_LIST(e_bullets_list,iter,to_delete);
    CLEAR_LIST(gal_attack_list,iter_gal_attacks,attack_to_delete);
    
    if(menu_status_register & BIT_INVADERS)
      delete invaders;
    if(menu_status_register & BIT_GALAXIANS)
      delete galaxians;
      
    delete player_1;
    delete player_2;
    
    if((menu_key == M_KEY_ESCAPE) && (!(menu_status_register & BIT_DEMO_MODE)))
      menu_key = 0;  //  Escape/quit-game was handled.
      
    return menu_key;
}

int menu::do_menu()
{
    int n_players;
    int menu_key = test_menu_keys();
    unsigned char old_menu_status_register;
    unsigned int old_sound_flag;
    unsigned char flag;

    pause_after_get_window_focus = 0;
    do
    {   /*  Loop to manage menu-states  */
        switch(menu_state)
        {
          case STATE_SHOW_CONTROLS:
            menu_key = show_controls();
            break;

          case STATE_SHOW_SCORES_TABLE:
            menu_key = show_scores_table();
            break;            
          case STATE_SHOW_HIGH_SCORE_TABLE:
            menu_key = show_high_score_table();
            break;
            
          case STATE_GAME_DEMO:
            old_menu_status_register = menu_status_register;
            old_sound_flag = sound_flag;
            menu_status_register |= BIT_DEMO_MODE;
            sound_flag = 0;         //  No sound on demo.
            SET_BULLETS_BITS(2);  //  Demo with default bullets value.
            /*  Set shields on if Invaders present in this demo.  */
            if(menu_status_register & BIT_INVADERS)
              menu_status_register |= BIT_SHIELDS;
            else
              CLEAR_SHIELDS;

            if(game_demo[GET_GAME_TYPE-1])
              menu_key = play_the_game(1);
            
            /*  Restore all values  */
            menu_status_register = old_menu_status_register;
            sound_flag = old_sound_flag;
            break;
            
          case STATE_PLAY_THE_GAME:
            menu_key = play_the_game(n_players);
            menu_state = STATE_SHOW_HIGH_SCORE_TABLE-1;
            break;
            
          case STATE_CHANGE_CONTROLS:
            menu_key = change_controls();
            menu_state = STATE_SHOW_CONTROLS-1;
            break;
            
          case STATE_SWITCH_GAME:
            flag = GET_GAME_TYPE;           
            if(flag == 3)
              flag = 1;
            else
              flag++;
  
            do_arcade_reset(flag);
            menu_state = menu_key = 0;
            break;
        }

        switch(menu_key)
        { /*  Handle Menu-input  */
          case M_KEY_SOUND:
            sound_flag = (sound_flag + sound_inc) & sound_expr;
            menu_state = STATE_SHOW_CONTROLS;
            break;
            
          case M_KEY_VSYNC:
            menu_status_register ^= BIT_VSYNC;
            menu_state = STATE_SHOW_CONTROLS;
            break;
            
          case M_KEY_SHIELDS:
            menu_status_register ^= BIT_SHIELDS;
            menu_state = STATE_SHOW_CONTROLS;
            break;
            
          case M_KEY_BULLETS:
            flag = GET_BULLETS_BITS;
            if(flag < 3)
              flag++;
            else
              flag = 0;
            
            SET_BULLETS_BITS(flag);
            menu_state = STATE_SHOW_CONTROLS;
            break;
            
          case M_KEY_CHANGE_CONTROLS:
            menu_state = STATE_CHANGE_CONTROLS;
            break;

          case M_KEY_SWITCH_GAME:
            menu_state = STATE_SWITCH_GAME;
            break;
            
          case M_KEY_START_2:
            n_players = 2;
            menu_state = STATE_PLAY_THE_GAME;
            break;

          case M_KEY_START_1:
            n_players = 1;
            menu_state = STATE_PLAY_THE_GAME;
            break;

          default:            
            menu_state++;
            if(menu_state > STATE_GAME_DEMO)
              menu_state = STATE_SHOW_CONTROLS;
        }
    }while(menu_key != M_KEY_ESCAPE);
 
    if(game_footer)
      destroy_bitmap(game_footer);   
      
    /*  Save the Hi-score table  */
    FILE *fp;
    char *file_name;
    file_name = (char *) malloc((strlen((*game_sprites).dir)) + (strlen(HIGH_SCORES_FILE_NAME))+1);
    sprintf(file_name,"%s%s",(*game_sprites).dir, HIGH_SCORES_FILE_NAME); 
    if((fp = fopen(file_name, "wb")) != NULL)
    {
      fwrite(&high_scores[0], sizeof(st_high_score_record), 15, fp);
      fclose(fp);
    }    
    free(file_name);
}

menu::~menu(void)
{
    int i;
    
    for(i = 0; i < NUMBER_OF_DEMOS; i++)
      if(game_demo[i])
        free(game_demo[i]);
        
    clear_menu_items();
    free(menu_keys);
    
    if(stars_layer_1)
      delete (stars_layer_1);
}

void menu::make_high_score(int h_score, char *text)
{
    char t_score[7];
      
    memset(text, '0', 7);
    sprintf(t_score,"%d", h_score);
    strcpy((text+6)-strlen(t_score), t_score);
}